diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7e5a0c19c9..b3fa6d4932 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,53 +4,60 @@ "postCreateCommand": [ "script/devcontainer-post-create" ], + "containerEnv": { + "DEVCONTAINER": "1" + }, "runArgs": [ "--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1" ], "appPort": 6052, - "extensions": [ - // python - "ms-python.python", - "visualstudioexptteam.vscodeintellicode", - // yaml - "redhat.vscode-yaml", - // cpp - "ms-vscode.cpptools", - // editorconfig - "editorconfig.editorconfig", - ], - "settings": { - "python.languageServer": "Pylance", - "python.pythonPath": "/usr/bin/python3", - "python.linting.pylintEnabled": true, - "python.linting.enabled": true, - "python.formatting.provider": "black", - "editor.formatOnPaste": false, - "editor.formatOnSave": true, - "editor.formatOnType": true, - "files.trimTrailingWhitespace": true, - "terminal.integrated.defaultProfile.linux": "bash", - "yaml.customTags": [ - "!secret scalar", - "!lambda scalar", - "!include_dir_named scalar", - "!include_dir_list scalar", - "!include_dir_merge_list scalar", - "!include_dir_merge_named scalar" - ], - "files.exclude": { - "**/.git": true, - "**/.DS_Store": true, - "**/*.pyc": { - "when": "$(basename).py" - }, - "**/__pycache__": true - }, - "files.associations": { - "**/.vscode/*.json": "jsonc" - }, - "C_Cpp.clang_format_path": "/usr/bin/clang-format-11", + "customizations": { + "vscode": { + "extensions": [ + // python + "ms-python.python", + "visualstudioexptteam.vscodeintellicode", + // yaml + "redhat.vscode-yaml", + // cpp + "ms-vscode.cpptools", + // editorconfig + "editorconfig.editorconfig", + ], + "settings": { + "python.languageServer": "Pylance", + "python.pythonPath": "/usr/bin/python3", + "python.linting.pylintEnabled": true, + "python.linting.enabled": true, + "python.formatting.provider": "black", + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true, + "terminal.integrated.defaultProfile.linux": "bash", + "yaml.customTags": [ + "!secret scalar", + "!lambda scalar", + "!include_dir_named scalar", + "!include_dir_list scalar", + "!include_dir_merge_list scalar", + "!include_dir_merge_named scalar" + ], + "files.exclude": { + "**/.git": true, + "**/.DS_Store": true, + "**/*.pyc": { + "when": "$(basename).py" + }, + "**/__pycache__": true + }, + "files.associations": { + "**/.vscode/*.json": "jsonc" + }, + "C_Cpp.clang_format_path": "/usr/bin/clang-format-13" + } + } } } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60c987f6c4..d22c2b7e03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,10 @@ jobs: file: tests/test3.yaml name: Test tests/test3.yaml pio_cache_key: test3 + - id: test + file: tests/test3.1.yaml + name: Test tests/test3.1.yaml + pio_cache_key: test3.1 - id: test file: tests/test4.yaml name: Test tests/test4.yaml @@ -129,7 +133,7 @@ jobs: - name: Install clang tools run: | sudo apt-get install \ - clang-format-11 \ + clang-format-13 \ clang-tidy-11 if: matrix.id == 'clang-tidy' || matrix.id == 'clang-format' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index a2ba086394..f5d291b49f 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,7 +18,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v7 + - uses: actions/stale@v8 with: days-before-pr-stale: 90 days-before-pr-close: 7 @@ -38,7 +38,7 @@ jobs: close-issues: runs-on: ubuntu-latest steps: - - uses: actions/stale@v7 + - uses: actions/stale@v8 with: days-before-pr-stale: -1 days-before-pr-close: -1 diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml new file mode 100644 index 0000000000..671fe1f21a --- /dev/null +++ b/.github/workflows/sync-device-classes.yml @@ -0,0 +1,60 @@ +--- +name: Synchronise Device Classes from Home Assistant + +on: + workflow_dispatch: + schedule: + - cron: '45 6 * * *' + +permissions: + contents: write + pull-requests: write + +jobs: + sync: + name: Sync Device Classes + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Checkout Home Assistant + uses: actions/checkout@v3 + with: + repository: home-assistant/core + path: lib/home-assistant + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Home Assistant + run: | + python -m pip install --upgrade pip + pip install -e lib/home-assistant + + - name: Sync + run: | + python ./script/sync-device_class.py + + - name: Get PR template + id: pr-template-body + run: | + body=$(cat .github/PULL_REQUEST_TEMPLATE.md) + delimiter="$(openssl rand -hex 8)" + echo "body<<$delimiter" >> $GITHUB_OUTPUT + echo "$body" >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT + + - name: Commit changes + uses: peter-evans/create-pull-request@v4 + with: + commit-message: "Synchronise Device Classes from Home Assistant" + committer: esphomebot + author: esphomebot + branch: sync/device-classes/ + branch-suffix: timestamp + delete-branch: true + title: "Synchronise Device Classes from Home Assistant" + body: ${{ steps.pr-template-body.outputs.body }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0de82cf2de..be82fc826b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,8 +2,8 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - - repo: https://github.com/ambv/black - rev: 23.1.0 + - repo: https://github.com/psf/black + rev: 23.3.0 hooks: - id: black args: diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b6584bc735..307dd496f0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,15 +2,24 @@ "version": "2.0.0", "tasks": [ { - "label": "run", + "label": "Run Dashboard", "type": "shell", - "command": "python3 -m esphome dashboard config/", + "command": "${command:python.interpreterPath}", + "args": [ + "-m", + "esphome", + "dashboard", + "config/" + ], "problemMatcher": [] }, { "label": "clang-tidy", "type": "shell", - "command": "./script/clang-tidy", + "command": "${command:python.interpreterPath}", + "args": [ + "./script/clang-tidy" + ], "problemMatcher": [ { "owner": "clang-tidy", diff --git a/CODEOWNERS b/CODEOWNERS index c006db2a6a..8e606d253a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -111,6 +111,8 @@ esphome/components/hte501/* @Stock-M esphome/components/hydreon_rgxx/* @functionpointer esphome/components/i2c/* @esphome/core esphome/components/i2s_audio/* @jesserockz +esphome/components/i2s_audio/media_player/* @jesserockz +esphome/components/i2s_audio/microphone/* @jesserockz esphome/components/ili9xxx/* @nielsnl68 esphome/components/improv_base/* @esphome/core esphome/components/improv_serial/* @esphome/core @@ -154,11 +156,13 @@ esphome/components/mcp9808/* @k7hpn esphome/components/md5/* @esphome/core esphome/components/mdns/* @esphome/core esphome/components/media_player/* @jesserockz +esphome/components/microphone/* @jesserockz esphome/components/mics_4514/* @jesserockz esphome/components/midea/* @dudanov esphome/components/midea_ir/* @dudanov esphome/components/mitsubishi/* @RubyBailey esphome/components/mlx90393/* @functionpointer +esphome/components/mmc5603/* @benhoff esphome/components/modbus_controller/* @martgras esphome/components/modbus_controller/binary_sensor/* @martgras esphome/components/modbus_controller/number/* @martgras @@ -286,6 +290,7 @@ esphome/components/ufire_ise/* @pvizeli esphome/components/ultrasonic/* @OttoWinter esphome/components/vbus/* @ssieb esphome/components/version/* @esphome/core +esphome/components/voice_assistant/* @jesserockz esphome/components/wake_on_lan/* @willwill2will54 esphome/components/web_server_base/* @OttoWinter esphome/components/whirlpool/* @glmnet diff --git a/docker/Dockerfile b/docker/Dockerfile index 59901d7b2c..383c73565d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -135,7 +135,7 @@ RUN \ apt-get update \ # Use pinned versions so that we get updates with build caching && apt-get install -y --no-install-recommends \ - clang-format-11=1:11.0.1-2 \ + clang-format-13=1:13.0.1-6~deb11u1 \ clang-tidy-11=1:11.0.1-2 \ patch=2.7.6-7 \ software-properties-common=0.96.20.2-2.1 \ diff --git a/esphome/__main__.py b/esphome/__main__.py index 24c2ce1d13..78320a05f0 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -152,6 +152,8 @@ def run_miniterm(config, port): _LOGGER.error("Could not connect to serial port %s", port) return 1 + return 0 + def wrap_to_code(name, comp): coro = coroutine(comp.to_code) diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index b09f5ab767..d0ef4a2fbb 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -65,7 +65,7 @@ void Am43Component::control(const CoverCall &call) { if (this->invert_position_) pos = 1 - pos; - auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100)); + auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t) (pos * 100)); auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 1cebdd0cbe..f31ef3ffc0 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -53,6 +53,9 @@ service APIConnection { rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {} rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {} rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {} + rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {} + + rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {} } @@ -208,6 +211,8 @@ message DeviceInfoResponse { string manufacturer = 12; string friendly_name = 13; + + uint32 voice_assistant_version = 14; } message ListEntitiesRequest { @@ -1125,6 +1130,7 @@ message MediaPlayerCommandRequest { message SubscribeBluetoothLEAdvertisementsRequest { option (id) = 66; option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; } message BluetoothServiceData { @@ -1156,6 +1162,7 @@ enum BluetoothDeviceRequestType { BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3; BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4; BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5; + BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6; } message BluetoothDeviceRequest { @@ -1359,3 +1366,71 @@ message BluetoothDeviceUnpairingResponse { bool success = 2; int32 error = 3; } + +message UnsubscribeBluetoothLEAdvertisementsRequest { + option (id) = 87; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; +} + +message BluetoothDeviceClearCacheResponse { + option (id) = 88; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + bool success = 2; + int32 error = 3; +} + +// ==================== PUSH TO TALK ==================== +message SubscribeVoiceAssistantRequest { + option (id) = 89; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + bool subscribe = 1; +} + +message VoiceAssistantRequest { + option (id) = 90; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + bool start = 1; +} + +message VoiceAssistantResponse { + option (id) = 91; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + uint32 port = 1; + bool error = 2; +} + +enum VoiceAssistantEvent { + VOICE_ASSISTANT_ERROR = 0; + VOICE_ASSISTANT_RUN_START = 1; + VOICE_ASSISTANT_RUN_END = 2; + VOICE_ASSISTANT_STT_START = 3; + VOICE_ASSISTANT_STT_END = 4; + VOICE_ASSISTANT_INTENT_START = 5; + VOICE_ASSISTANT_INTENT_END = 6; + VOICE_ASSISTANT_TTS_START = 7; + VOICE_ASSISTANT_TTS_END = 8; +} + +message VoiceAssistantEventData { + string name = 1; + string value = 2; +} + +message VoiceAssistantEventResponse { + option (id) = 92; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + VoiceAssistantEvent event_type = 1; + repeated VoiceAssistantEventData data = 2; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 40a5a230a5..96fb3ea9fa 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1,5 +1,6 @@ #include "api_connection.h" #include +#include #include "esphome/components/network/util.h" #include "esphome/core/entity_base.h" #include "esphome/core/hal.h" @@ -15,6 +16,9 @@ #ifdef USE_BLUETOOTH_PROXY #include "esphome/components/bluetooth_proxy/bluetooth_proxy.h" #endif +#ifdef USE_VOICE_ASSISTANT +#include "esphome/components/voice_assistant/voice_assistant.h" +#endif namespace esphome { namespace api { @@ -180,7 +184,8 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_ ListEntitiesBinarySensorResponse msg; msg.object_id = binary_sensor->get_object_id(); msg.key = binary_sensor->get_object_id_hash(); - msg.name = binary_sensor->get_name(); + if (binary_sensor->has_own_name()) + msg.name = binary_sensor->get_name(); msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor); msg.device_class = binary_sensor->get_device_class(); msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor(); @@ -212,7 +217,8 @@ bool APIConnection::send_cover_info(cover::Cover *cover) { ListEntitiesCoverResponse msg; msg.key = cover->get_object_id_hash(); msg.object_id = cover->get_object_id(); - msg.name = cover->get_name(); + if (cover->has_own_name()) + msg.name = cover->get_name(); msg.unique_id = get_default_unique_id("cover", cover); msg.assumed_state = traits.get_is_assumed_state(); msg.supports_position = traits.get_supports_position(); @@ -275,7 +281,8 @@ bool APIConnection::send_fan_info(fan::Fan *fan) { ListEntitiesFanResponse msg; msg.key = fan->get_object_id_hash(); msg.object_id = fan->get_object_id(); - msg.name = fan->get_name(); + if (fan->has_own_name()) + msg.name = fan->get_name(); msg.unique_id = get_default_unique_id("fan", fan); msg.supports_oscillation = traits.supports_oscillation(); msg.supports_speed = traits.supports_speed(); @@ -337,7 +344,8 @@ bool APIConnection::send_light_info(light::LightState *light) { ListEntitiesLightResponse msg; msg.key = light->get_object_id_hash(); msg.object_id = light->get_object_id(); - msg.name = light->get_name(); + if (light->has_own_name()) + msg.name = light->get_name(); msg.unique_id = get_default_unique_id("light", light); msg.disabled_by_default = light->is_disabled_by_default(); @@ -418,7 +426,8 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { ListEntitiesSensorResponse msg; msg.key = sensor->get_object_id_hash(); msg.object_id = sensor->get_object_id(); - msg.name = sensor->get_name(); + if (sensor->has_own_name()) + msg.name = sensor->get_name(); msg.unique_id = sensor->unique_id(); if (msg.unique_id.empty()) msg.unique_id = get_default_unique_id("sensor", sensor); @@ -448,7 +457,8 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) { ListEntitiesSwitchResponse msg; msg.key = a_switch->get_object_id_hash(); msg.object_id = a_switch->get_object_id(); - msg.name = a_switch->get_name(); + if (a_switch->has_own_name()) + msg.name = a_switch->get_name(); msg.unique_id = get_default_unique_id("switch", a_switch); msg.icon = a_switch->get_icon(); msg.assumed_state = a_switch->assumed_state(); @@ -533,7 +543,8 @@ bool APIConnection::send_climate_info(climate::Climate *climate) { ListEntitiesClimateResponse msg; msg.key = climate->get_object_id_hash(); msg.object_id = climate->get_object_id(); - msg.name = climate->get_name(); + if (climate->has_own_name()) + msg.name = climate->get_name(); msg.unique_id = get_default_unique_id("climate", climate); msg.disabled_by_default = climate->is_disabled_by_default(); @@ -611,7 +622,8 @@ bool APIConnection::send_number_info(number::Number *number) { ListEntitiesNumberResponse msg; msg.key = number->get_object_id_hash(); msg.object_id = number->get_object_id(); - msg.name = number->get_name(); + if (number->has_own_name()) + msg.name = number->get_name(); msg.unique_id = get_default_unique_id("number", number); msg.icon = number->get_icon(); msg.disabled_by_default = number->is_disabled_by_default(); @@ -652,7 +664,8 @@ bool APIConnection::send_select_info(select::Select *select) { ListEntitiesSelectResponse msg; msg.key = select->get_object_id_hash(); msg.object_id = select->get_object_id(); - msg.name = select->get_name(); + if (select->has_own_name()) + msg.name = select->get_name(); msg.unique_id = get_default_unique_id("select", select); msg.icon = select->get_icon(); msg.disabled_by_default = select->is_disabled_by_default(); @@ -679,7 +692,8 @@ bool APIConnection::send_button_info(button::Button *button) { ListEntitiesButtonResponse msg; msg.key = button->get_object_id_hash(); msg.object_id = button->get_object_id(); - msg.name = button->get_name(); + if (button->has_own_name()) + msg.name = button->get_name(); msg.unique_id = get_default_unique_id("button", button); msg.icon = button->get_icon(); msg.disabled_by_default = button->is_disabled_by_default(); @@ -710,7 +724,8 @@ bool APIConnection::send_lock_info(lock::Lock *a_lock) { ListEntitiesLockResponse msg; msg.key = a_lock->get_object_id_hash(); msg.object_id = a_lock->get_object_id(); - msg.name = a_lock->get_name(); + if (a_lock->has_own_name()) + msg.name = a_lock->get_name(); msg.unique_id = get_default_unique_id("lock", a_lock); msg.icon = a_lock->get_icon(); msg.assumed_state = a_lock->traits.get_assumed_state(); @@ -755,7 +770,8 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play ListEntitiesMediaPlayerResponse msg; msg.key = media_player->get_object_id_hash(); msg.object_id = media_player->get_object_id(); - msg.name = media_player->get_name(); + if (media_player->has_own_name()) + msg.name = media_player->get_name(); msg.unique_id = get_default_unique_id("media_player", media_player); msg.icon = media_player->get_icon(); msg.disabled_by_default = media_player->is_disabled_by_default(); @@ -799,7 +815,8 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { ListEntitiesCameraResponse msg; msg.key = camera->get_object_id_hash(); msg.object_id = camera->get_object_id(); - msg.name = camera->get_name(); + if (camera->has_own_name()) + msg.name = camera->get_name(); msg.unique_id = get_default_unique_id("camera", camera); msg.disabled_by_default = camera->is_disabled_by_default(); msg.icon = camera->get_icon(); @@ -879,6 +896,30 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_ } #endif +#ifdef USE_VOICE_ASSISTANT +bool APIConnection::request_voice_assistant(bool start) { + if (!this->voice_assistant_subscription_) + return false; + VoiceAssistantRequest msg; + msg.start = start; + return this->send_voice_assistant_request(msg); +} +void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) { + if (voice_assistant::global_voice_assistant != nullptr) { + struct sockaddr_storage storage; + socklen_t len = sizeof(storage); + this->helper_->getpeername((struct sockaddr *) &storage, &len); + voice_assistant::global_voice_assistant->start(&storage, msg.port); + } +}; +void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) { + if (voice_assistant::global_voice_assistant != nullptr) { + voice_assistant::global_voice_assistant->on_event(msg); + } +} + +#endif + bool APIConnection::send_log_message(int level, const char *tag, const char *line) { if (this->log_subscription_ < level) return false; @@ -898,7 +939,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) { this->helper_->set_log_info(client_info_); this->client_api_version_major_ = msg.api_version_major; this->client_api_version_minor_ = msg.api_version_minor; - ESP_LOGV(TAG, "Hello from client: '%s' | API Version %d.%d", this->client_info_.c_str(), + ESP_LOGV(TAG, "Hello from client: '%s' | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(), this->client_api_version_major_, this->client_api_version_minor_); HelloResponse resp; @@ -953,7 +994,12 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { resp.webserver_port = USE_WEBSERVER_PORT; #endif #ifdef USE_BLUETOOTH_PROXY - resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 4 : 1; + resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() + ? bluetooth_proxy::ACTIVE_CONNECTIONS_VERSION + : bluetooth_proxy::PASSIVE_ONLY_VERSION; +#endif +#ifdef USE_VOICE_ASSISTANT + resp.voice_assistant_version = 1; #endif return resp; } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index b2076635b0..78ecbb98e6 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -6,6 +6,7 @@ #include "api_server.h" #include "esphome/core/application.h" #include "esphome/core/component.h" +#include "esphome/core/defines.h" #include @@ -97,6 +98,12 @@ class APIConnection : public APIServerConnection { this->send_homeassistant_service_response(call); } #ifdef USE_BLUETOOTH_PROXY + void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override { + this->bluetooth_le_advertisement_subscription_ = true; + } + void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override { + this->bluetooth_le_advertisement_subscription_ = false; + } bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg); void bluetooth_device_request(const BluetoothDeviceRequest &msg) override; @@ -117,6 +124,15 @@ class APIConnection : public APIServerConnection { } #endif +#ifdef USE_VOICE_ASSISTANT + void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override { + this->voice_assistant_subscription_ = msg.subscribe; + } + bool request_voice_assistant(bool start); + void on_voice_assistant_response(const VoiceAssistantResponse &msg) override; + void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override; +#endif + void on_disconnect_response(const DisconnectResponse &value) override; void on_ping_response(const PingResponse &value) override { // we initiated ping @@ -150,9 +166,7 @@ class APIConnection : public APIServerConnection { return {}; } void execute_service(const ExecuteServiceRequest &msg) override; - void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override { - this->bluetooth_le_advertisement_subscription_ = true; - } + bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; } bool is_connection_setup() override { return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated(); @@ -197,7 +211,12 @@ class APIConnection : public APIServerConnection { uint32_t last_traffic_; bool sent_ping_{false}; bool service_call_subscription_{false}; +#ifdef USE_BLUETOOTH_PROXY bool bluetooth_le_advertisement_subscription_{false}; +#endif +#ifdef USE_VOICE_ASSISTANT + bool voice_assistant_subscription_{false}; +#endif bool next_close_ = false; APIServer *parent_; InitialStateIterator initial_state_iterator_; diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index c18e045a99..f4b18a1fd6 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -295,7 +295,7 @@ APIError APINoiseFrameHelper::state_action_() { if (aerr != APIError::OK) return aerr; // ignore contents, may be used in future for flags - prologue_.push_back((uint8_t)(frame.msg.size() >> 8)); + prologue_.push_back((uint8_t) (frame.msg.size() >> 8)); prologue_.push_back((uint8_t) frame.msg.size()); prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end()); @@ -492,9 +492,9 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload // tmpbuf[1], tmpbuf[2] to be set later const uint8_t msg_offset = 3; const uint8_t payload_offset = msg_offset + 4; - tmpbuf[msg_offset + 0] = (uint8_t)(type >> 8); // type + tmpbuf[msg_offset + 0] = (uint8_t) (type >> 8); // type tmpbuf[msg_offset + 1] = (uint8_t) type; - tmpbuf[msg_offset + 2] = (uint8_t)(payload_len >> 8); // data_len + tmpbuf[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len tmpbuf[msg_offset + 3] = (uint8_t) payload_len; // copy data std::copy(payload, payload + payload_len, &tmpbuf[payload_offset]); @@ -512,7 +512,7 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload } size_t total_len = 3 + mbuf.size; - tmpbuf[1] = (uint8_t)(mbuf.size >> 8); + tmpbuf[1] = (uint8_t) (mbuf.size >> 8); tmpbuf[2] = (uint8_t) mbuf.size; struct iovec iov; @@ -610,7 +610,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) { uint8_t header[3]; header[0] = 0x01; // indicator - header[1] = (uint8_t)(len >> 8); + header[1] = (uint8_t) (len >> 8); header[2] = (uint8_t) len; struct iovec iov[2]; diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 348a9b574f..bf4872d2d6 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -10,8 +10,8 @@ #include "noise/protocol.h" #endif -#include "esphome/components/socket/socket.h" #include "api_noise_context.h" +#include "esphome/components/socket/socket.h" namespace esphome { namespace api { @@ -67,6 +67,7 @@ class APIFrameHelper { virtual bool can_write_without_blocking() = 0; virtual APIError write_packet(uint16_t type, const uint8_t *data, size_t len) = 0; virtual std::string getpeername() = 0; + virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0; virtual APIError close() = 0; virtual APIError shutdown(int how) = 0; // Give this helper a name for logging @@ -84,7 +85,10 @@ class APINoiseFrameHelper : public APIFrameHelper { APIError read_packet(ReadPacketBuffer *buffer) override; bool can_write_without_blocking() override; APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override; - std::string getpeername() override { return socket_->getpeername(); } + std::string getpeername() override { return this->socket_->getpeername(); } + int getpeername(struct sockaddr *addr, socklen_t *addrlen) override { + return this->socket_->getpeername(addr, addrlen); + } APIError close() override; APIError shutdown(int how) override; // Give this helper a name for logging @@ -144,7 +148,10 @@ class APIPlaintextFrameHelper : public APIFrameHelper { APIError read_packet(ReadPacketBuffer *buffer) override; bool can_write_without_blocking() override; APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override; - std::string getpeername() override { return socket_->getpeername(); } + std::string getpeername() override { return this->socket_->getpeername(); } + int getpeername(struct sockaddr *addr, socklen_t *addrlen) override { + return this->socket_->getpeername(addr, addrlen); + } APIError close() override; APIError shutdown(int how) override; // Give this helper a name for logging diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 381f8b3c46..334cde16b3 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -400,6 +400,34 @@ const char *proto_enum_to_string(enums::Bluet return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE"; case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE: return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE"; + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE"; + default: + return "UNKNOWN"; + } +} +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP +template<> const char *proto_enum_to_string(enums::VoiceAssistantEvent value) { + switch (value) { + case enums::VOICE_ASSISTANT_ERROR: + return "VOICE_ASSISTANT_ERROR"; + case enums::VOICE_ASSISTANT_RUN_START: + return "VOICE_ASSISTANT_RUN_START"; + case enums::VOICE_ASSISTANT_RUN_END: + return "VOICE_ASSISTANT_RUN_END"; + case enums::VOICE_ASSISTANT_STT_START: + return "VOICE_ASSISTANT_STT_START"; + case enums::VOICE_ASSISTANT_STT_END: + return "VOICE_ASSISTANT_STT_END"; + case enums::VOICE_ASSISTANT_INTENT_START: + return "VOICE_ASSISTANT_INTENT_START"; + case enums::VOICE_ASSISTANT_INTENT_END: + return "VOICE_ASSISTANT_INTENT_END"; + case enums::VOICE_ASSISTANT_TTS_START: + return "VOICE_ASSISTANT_TTS_START"; + case enums::VOICE_ASSISTANT_TTS_END: + return "VOICE_ASSISTANT_TTS_END"; default: return "UNKNOWN"; } @@ -592,6 +620,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { this->bluetooth_proxy_version = value.as_uint32(); return true; } + case 14: { + this->voice_assistant_version = value.as_uint32(); + return true; + } default: return false; } @@ -652,6 +684,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(11, this->bluetooth_proxy_version); buffer.encode_string(12, this->manufacturer); buffer.encode_string(13, this->friendly_name); + buffer.encode_uint32(14, this->voice_assistant_version); } #ifdef HAS_PROTO_MESSAGE_DUMP void DeviceInfoResponse::dump_to(std::string &out) const { @@ -710,6 +743,11 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append(" friendly_name: "); out.append("'").append(this->friendly_name).append("'"); out.append("\n"); + + out.append(" voice_assistant_version: "); + sprintf(buffer, "%u", this->voice_assistant_version); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -6060,6 +6098,204 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const { out.append("}"); } #endif +void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {} +#ifdef HAS_PROTO_MESSAGE_DUMP +void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { + out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}"); +} +#endif +bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->success = value.as_bool(); + return true; + } + case 3: { + this->error = value.as_int32(); + return true; + } + default: + return false; + } +} +void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_bool(2, this->success); + buffer.encode_int32(3, this->error); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothDeviceClearCacheResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" success: "); + out.append(YESNO(this->success)); + out.append("\n"); + + out.append(" error: "); + sprintf(buffer, "%d", this->error); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->subscribe = value.as_bool(); + return true; + } + default: + return false; + } +} +void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->subscribe); } +#ifdef HAS_PROTO_MESSAGE_DUMP +void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("SubscribeVoiceAssistantRequest {\n"); + out.append(" subscribe: "); + out.append(YESNO(this->subscribe)); + out.append("\n"); + out.append("}"); +} +#endif +bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->start = value.as_bool(); + return true; + } + default: + return false; + } +} +void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->start); } +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantRequest {\n"); + out.append(" start: "); + out.append(YESNO(this->start)); + out.append("\n"); + out.append("}"); +} +#endif +bool VoiceAssistantResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->port = value.as_uint32(); + return true; + } + case 2: { + this->error = value.as_bool(); + return true; + } + default: + return false; + } +} +void VoiceAssistantResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint32(1, this->port); + buffer.encode_bool(2, this->error); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantResponse {\n"); + out.append(" port: "); + sprintf(buffer, "%u", this->port); + out.append(buffer); + out.append("\n"); + + out.append(" error: "); + out.append(YESNO(this->error)); + out.append("\n"); + out.append("}"); +} +#endif +bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->name = value.as_string(); + return true; + } + case 2: { + this->value = value.as_string(); + return true; + } + default: + return false; + } +} +void VoiceAssistantEventData::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->name); + buffer.encode_string(2, this->value); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantEventData::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantEventData {\n"); + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" value: "); + out.append("'").append(this->value).append("'"); + out.append("\n"); + out.append("}"); +} +#endif +bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->event_type = value.as_enum(); + return true; + } + default: + return false; + } +} +bool VoiceAssistantEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->data.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_enum(1, this->event_type); + for (auto &it : this->data) { + buffer.encode_message(2, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantEventResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantEventResponse {\n"); + out.append(" event_type: "); + out.append(proto_enum_to_string(this->event_type)); + out.append("\n"); + + for (const auto &it : this->data) { + out.append(" data: "); + it.dump_to(out); + out.append("\n"); + } + out.append("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index e9025142e9..9f71c07913 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -163,6 +163,18 @@ enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3, BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4, BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, + BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6, +}; +enum VoiceAssistantEvent : uint32_t { + VOICE_ASSISTANT_ERROR = 0, + VOICE_ASSISTANT_RUN_START = 1, + VOICE_ASSISTANT_RUN_END = 2, + VOICE_ASSISTANT_STT_START = 3, + VOICE_ASSISTANT_STT_END = 4, + VOICE_ASSISTANT_INTENT_START = 5, + VOICE_ASSISTANT_INTENT_END = 6, + VOICE_ASSISTANT_TTS_START = 7, + VOICE_ASSISTANT_TTS_END = 8, }; } // namespace enums @@ -278,6 +290,7 @@ class DeviceInfoResponse : public ProtoMessage { uint32_t bluetooth_proxy_version{0}; std::string manufacturer{}; std::string friendly_name{}; + uint32_t voice_assistant_version{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -1554,6 +1567,87 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class UnsubscribeBluetoothLEAdvertisementsRequest : 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 BluetoothDeviceClearCacheResponse : public ProtoMessage { + public: + uint64_t address{0}; + bool success{false}; + int32_t error{0}; + 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 SubscribeVoiceAssistantRequest : public ProtoMessage { + public: + bool subscribe{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 VoiceAssistantRequest : public ProtoMessage { + public: + bool start{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 VoiceAssistantResponse : public ProtoMessage { + public: + uint32_t port{0}; + bool error{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 VoiceAssistantEventData : public ProtoMessage { + public: + std::string name{}; + std::string value{}; + 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 VoiceAssistantEventResponse : public ProtoMessage { + public: + enums::VoiceAssistantEvent event_type{}; + std::vector data{}; + 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; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 7ee9e56192..df36d0fdea 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -329,6 +329,8 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer #ifdef USE_MEDIA_PLAYER #endif #ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) { #ifdef HAS_PROTO_MESSAGE_DUMP ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str()); @@ -441,6 +443,30 @@ bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const Blu return this->send_message_(msg, 86); } #endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 88); +} +#endif +#ifdef USE_VOICE_ASSISTANT +#endif +#ifdef USE_VOICE_ASSISTANT +bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 90); +} +#endif +#ifdef USE_VOICE_ASSISTANT +#endif +#ifdef USE_VOICE_ASSISTANT +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -709,12 +735,14 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, break; } case 66: { +#ifdef USE_BLUETOOTH_PROXY SubscribeBluetoothLEAdvertisementsRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str()); #endif this->on_subscribe_bluetooth_le_advertisements_request(msg); +#endif break; } case 68: { @@ -802,6 +830,50 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str()); #endif this->on_subscribe_bluetooth_connections_free_request(msg); +#endif + break; + } + case 87: { +#ifdef USE_BLUETOOTH_PROXY + UnsubscribeBluetoothLEAdvertisementsRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str()); +#endif + this->on_unsubscribe_bluetooth_le_advertisements_request(msg); +#endif + break; + } + case 89: { +#ifdef USE_VOICE_ASSISTANT + SubscribeVoiceAssistantRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_subscribe_voice_assistant_request: %s", msg.dump().c_str()); +#endif + this->on_subscribe_voice_assistant_request(msg); +#endif + break; + } + case 91: { +#ifdef USE_VOICE_ASSISTANT + VoiceAssistantResponse msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_voice_assistant_response: %s", msg.dump().c_str()); +#endif + this->on_voice_assistant_response(msg); +#endif + break; + } + case 92: { +#ifdef USE_VOICE_ASSISTANT + VoiceAssistantEventResponse msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str()); +#endif + this->on_voice_assistant_event_response(msg); #endif break; } @@ -1065,6 +1137,7 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma this->media_player_command(msg); } #endif +#ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( const SubscribeBluetoothLEAdvertisementsRequest &msg) { if (!this->is_connection_setup()) { @@ -1077,6 +1150,7 @@ void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( } this->subscribe_bluetooth_le_advertisements(msg); } +#endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) { if (!this->is_connection_setup()) { @@ -1185,6 +1259,33 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request( } } #endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request( + const UnsubscribeBluetoothLEAdvertisementsRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->unsubscribe_bluetooth_le_advertisements(msg); +} +#endif +#ifdef USE_VOICE_ASSISTANT +void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->subscribe_voice_assistant(msg); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index f1879b2dba..3808f128a4 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -154,8 +154,10 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_MEDIA_PLAYER virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){}; #endif +#ifdef USE_BLUETOOTH_PROXY virtual void on_subscribe_bluetooth_le_advertisements_request( const SubscribeBluetoothLEAdvertisementsRequest &value){}; +#endif #ifdef USE_BLUETOOTH_PROXY bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg); #endif @@ -215,6 +217,25 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_BLUETOOTH_PROXY bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_unsubscribe_bluetooth_le_advertisements_request( + const UnsubscribeBluetoothLEAdvertisementsRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg); +#endif +#ifdef USE_VOICE_ASSISTANT + virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){}; +#endif +#ifdef USE_VOICE_ASSISTANT + bool send_voice_assistant_request(const VoiceAssistantRequest &msg); +#endif +#ifdef USE_VOICE_ASSISTANT + virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){}; +#endif +#ifdef USE_VOICE_ASSISTANT + virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){}; #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; @@ -267,7 +288,9 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_MEDIA_PLAYER virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0; #endif +#ifdef USE_BLUETOOTH_PROXY virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0; +#endif #ifdef USE_BLUETOOTH_PROXY virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0; #endif @@ -292,6 +315,12 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_BLUETOOTH_PROXY virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free( const SubscribeBluetoothConnectionsFreeRequest &msg) = 0; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0; +#endif +#ifdef USE_VOICE_ASSISTANT + virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0; #endif protected: void on_hello_request(const HelloRequest &msg) override; @@ -339,7 +368,9 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_MEDIA_PLAYER void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override; #endif +#ifdef USE_BLUETOOTH_PROXY void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; +#endif #ifdef USE_BLUETOOTH_PROXY void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override; #endif @@ -364,6 +395,13 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_BLUETOOTH_PROXY void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override; #endif +#ifdef USE_BLUETOOTH_PROXY + void on_unsubscribe_bluetooth_le_advertisements_request( + const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override; +#endif +#ifdef USE_VOICE_ASSISTANT + void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override; +#endif }; } // namespace api diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 6e28637241..97a7d6fbf6 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -45,7 +45,7 @@ void APIServer::setup() { struct sockaddr_storage server; - socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_)); + socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_); if (sl == 0) { ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno); this->mark_failed(); @@ -331,6 +331,17 @@ void APIServer::send_bluetooth_device_unpairing(uint64_t address, bool success, } } +void APIServer::send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error) { + BluetoothDeviceClearCacheResponse call; + call.address = address; + call.success = success; + call.error = error; + + for (auto &client : this->clients_) { + client->send_bluetooth_device_clear_cache_response(call); + } +} + void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) { BluetoothConnectionsFreeResponse call; call.free = free; @@ -416,5 +427,20 @@ void APIServer::on_shutdown() { delay(10); } +#ifdef USE_VOICE_ASSISTANT +bool APIServer::start_voice_assistant() { + bool result = false; + for (auto &c : this->clients_) { + result |= c->request_voice_assistant(true); + } + return result; +} +void APIServer::stop_voice_assistant() { + for (auto &c : this->clients_) { + c->request_voice_assistant(false); + } +} +#endif + } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 5f92e6b058..a1bec2802f 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -80,6 +80,7 @@ class APIServer : public Component, public Controller { void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK); void send_bluetooth_device_pairing(uint64_t address, bool paired, esp_err_t error = ESP_OK); void send_bluetooth_device_unpairing(uint64_t address, bool success, esp_err_t error = ESP_OK); + void send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error = ESP_OK); void send_bluetooth_connections_free(uint8_t free, uint8_t limit); void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call); void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call); @@ -94,6 +95,11 @@ class APIServer : public Component, public Controller { void request_time(); #endif +#ifdef USE_VOICE_ASSISTANT + bool start_voice_assistant(); + void stop_voice_assistant(); +#endif + bool is_connected() const; struct HomeAssistantStateSubscription { diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index ca7a4c0887..7af2e92534 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -1,4 +1,5 @@ #include "proto.h" +#include #include "esphome/core/log.h" namespace esphome { @@ -13,7 +14,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) { uint32_t consumed; auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed); if (!res.has_value()) { - ESP_LOGV(TAG, "Invalid field start at %u", i); + ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i); break; } @@ -25,12 +26,12 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) { case 0: { // VarInt res = ProtoVarInt::parse(&buffer[i], length - i, &consumed); if (!res.has_value()) { - ESP_LOGV(TAG, "Invalid VarInt at %u", i); + ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i); error = true; break; } if (!this->decode_varint(field_id, *res)) { - ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, res->as_uint32()); + ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32()); } i += consumed; break; @@ -38,38 +39,38 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) { case 2: { // Length-delimited res = ProtoVarInt::parse(&buffer[i], length - i, &consumed); if (!res.has_value()) { - ESP_LOGV(TAG, "Invalid Length Delimited at %u", i); + ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i); error = true; break; } uint32_t field_length = res->as_uint32(); i += consumed; if (field_length > length - i) { - ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %u", i); + ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i); error = true; break; } if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) { - ESP_LOGV(TAG, "Cannot decode Length Delimited field %u!", field_id); + ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id); } i += field_length; break; } case 5: { // 32-bit if (length - i < 4) { - ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %u", i); + ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %" PRIu32, i); error = true; break; } uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]); if (!this->decode_32bit(field_id, Proto32Bit(val))) { - ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val); + ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val); } i += 4; break; } default: - ESP_LOGV(TAG, "Invalid field type at %u", i); + ESP_LOGV(TAG, "Invalid field type at %" PRIu32, i); error = true; break; } diff --git a/esphome/components/as3935/__init__.py b/esphome/components/as3935/__init__.py index 0951d01e68..cf0580ca62 100644 --- a/esphome/components/as3935/__init__.py +++ b/esphome/components/as3935/__init__.py @@ -12,7 +12,6 @@ from esphome.const import ( CONF_CAPACITANCE, ) -AUTO_LOAD = ["sensor", "binary_sensor"] MULTI_CONF = True CONF_AS3935_ID = "as3935_id" diff --git a/esphome/components/as3935/as3935.cpp b/esphome/components/as3935/as3935.cpp index b36856218a..c5651caee3 100644 --- a/esphome/components/as3935/as3935.cpp +++ b/esphome/components/as3935/as3935.cpp @@ -26,9 +26,13 @@ void AS3935Component::setup() { void AS3935Component::dump_config() { ESP_LOGCONFIG(TAG, "AS3935:"); LOG_PIN(" Interrupt Pin: ", this->irq_pin_); +#ifdef USE_BINARY_SENSOR LOG_BINARY_SENSOR(" ", "Thunder alert", this->thunder_alert_binary_sensor_); +#endif +#ifdef USE_SENSOR LOG_SENSOR(" ", "Distance", this->distance_sensor_); LOG_SENSOR(" ", "Lightning energy", this->energy_sensor_); +#endif } float AS3935Component::get_setup_priority() const { return setup_priority::DATA; } @@ -44,16 +48,22 @@ void AS3935Component::loop() { ESP_LOGI(TAG, "Disturber was detected - try increasing the spike rejection value!"); } else if (int_value == LIGHTNING_INT) { ESP_LOGI(TAG, "Lightning has been detected!"); - if (this->thunder_alert_binary_sensor_ != nullptr) +#ifdef USE_BINARY_SENSOR + if (this->thunder_alert_binary_sensor_ != nullptr) { this->thunder_alert_binary_sensor_->publish_state(true); + this->set_timeout(10, [this]() { this->thunder_alert_binary_sensor_->publish_state(false); }); + } +#endif +#ifdef USE_SENSOR uint8_t distance = this->get_distance_to_storm_(); if (this->distance_sensor_ != nullptr) this->distance_sensor_->publish_state(distance); + uint32_t energy = this->get_lightning_energy_(); if (this->energy_sensor_ != nullptr) this->energy_sensor_->publish_state(energy); +#endif } - this->thunder_alert_binary_sensor_->publish_state(false); } void AS3935Component::write_indoor(bool indoor) { diff --git a/esphome/components/as3935/as3935.h b/esphome/components/as3935/as3935.h index 2cba9b11a0..a8af703a59 100644 --- a/esphome/components/as3935/as3935.h +++ b/esphome/components/as3935/as3935.h @@ -1,9 +1,14 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/defines.h" #include "esphome/core/hal.h" +#ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" +#endif +#ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" +#endif namespace esphome { namespace as3935 { @@ -52,6 +57,15 @@ enum AS3935Values { }; class AS3935Component : public Component { +#ifdef USE_SENSOR + SUB_SENSOR(distance) + SUB_SENSOR(energy) +#endif + +#ifdef USE_BINARY_SENSOR + SUB_BINARY_SENSOR(thunder_alert) +#endif + public: void setup() override; void dump_config() override; @@ -59,11 +73,7 @@ class AS3935Component : public Component { void loop() override; void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; } - void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; } - void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; } - void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) { - thunder_alert_binary_sensor_ = thunder_alert_binary_sensor; - } + void set_indoor(bool indoor) { indoor_ = indoor; } void write_indoor(bool indoor); void set_noise_level(uint8_t noise_level) { noise_level_ = noise_level; } @@ -92,9 +102,6 @@ class AS3935Component : public Component { virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) = 0; - sensor::Sensor *distance_sensor_{nullptr}; - sensor::Sensor *energy_sensor_{nullptr}; - binary_sensor::BinarySensor *thunder_alert_binary_sensor_{nullptr}; GPIOPin *irq_pin_; bool indoor_; diff --git a/esphome/components/bedjet/bedjet_hub.h b/esphome/components/bedjet/bedjet_hub.h index e4af2bca51..5809827cfa 100644 --- a/esphome/components/bedjet/bedjet_hub.h +++ b/esphome/components/bedjet/bedjet_hub.h @@ -116,7 +116,7 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo void update() override; void dump_config() override; void setup() override { this->codec_ = make_unique(); } - float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + float get_setup_priority() const override { return setup_priority::BLUETOOTH; } /** @return The BedJet's configured name, or the MAC address if not discovered yet. */ std::string get_name() { diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 1f837e3ac1..bd33b2af2d 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -41,16 +41,13 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) { this->state_callback_.call(state); } } -std::string BinarySensor::device_class() { return ""; } + BinarySensor::BinarySensor() : state(false) {} void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; } std::string BinarySensor::get_device_class() { if (this->device_class_.has_value()) return *this->device_class_; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - return this->device_class(); -#pragma GCC diagnostic pop + return ""; } void BinarySensor::add_filter(Filter *filter) { filter->parent_ = this; diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index fbfff63a38..0bf8cf2cdc 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -80,14 +80,6 @@ class BinarySensor : public EntityBase { virtual bool is_status_binary_sensor() const; - // ========== OVERRIDE METHODS ========== - // (You'll only need this when creating your own custom binary sensor) - /** Override this to set the default device class. - * - * @deprecated This method is deprecated, set the property during config validation instead. (2022.1) - */ - virtual std::string device_class(); - protected: CallbackManager state_callback_{}; optional device_class_{}; ///< Stores the override of the device class diff --git a/esphome/components/binary_sensor_map/binary_sensor_map.cpp b/esphome/components/binary_sensor_map/binary_sensor_map.cpp index b2dffaf74d..3934e0a99c 100644 --- a/esphome/components/binary_sensor_map/binary_sensor_map.cpp +++ b/esphome/components/binary_sensor_map/binary_sensor_map.cpp @@ -30,7 +30,7 @@ void BinarySensorMap::process_group_() { if (bs.binary_sensor->state) { num_active_sensors++; total_current_value += bs.sensor_value; - mask |= 1 << i; + mask |= 1ULL << i; } } // check if the sensor map was touched @@ -38,12 +38,11 @@ void BinarySensorMap::process_group_() { // did the bit_mask change or is it a new sensor touch if (this->last_mask_ != mask) { float publish_value = total_current_value / num_active_sensors; - ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value); this->publish_state(publish_value); } } else if (this->last_mask_ != 0ULL) { // is this a new sensor release - ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str()); + ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str()); this->publish_state(NAN); } this->last_mask_ = mask; @@ -52,28 +51,22 @@ void BinarySensorMap::process_group_() { void BinarySensorMap::process_sum_() { float total_current_value = 0.0; uint64_t mask = 0x00; - // check all binary_sensors for its state. when active add its value to total_current_value. - // create a bitmask for the binary_sensor status on all channels + // - check all binary_sensor states + // - if active, add its value to total_current_value + // - creates a bitmask for the binary_sensor status on all channels for (size_t i = 0; i < this->channels_.size(); i++) { auto bs = this->channels_[i]; if (bs.binary_sensor->state) { total_current_value += bs.sensor_value; - mask |= 1 << i; + mask |= 1ULL << i; } } - // check if the sensor map was touched - if (mask != 0ULL) { - // did the bit_mask change or is it a new sensor touch - if (this->last_mask_ != mask) { - float publish_value = total_current_value; - ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value); - this->publish_state(publish_value); - } - } else if (this->last_mask_ != 0ULL) { - // is this a new sensor release - ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing 0", this->name_.c_str()); - this->publish_state(0.0); + + // update state only if the binary sensor states have changed or if no state has ever been sent on boot + if ((this->last_mask_ != mask) || (!this->has_state())) { + this->publish_state(total_current_value); } + this->last_mask_ = mask; } diff --git a/esphome/components/binary_sensor_map/sensor.py b/esphome/components/binary_sensor_map/sensor.py index 025be490cd..573cce9223 100644 --- a/esphome/components/binary_sensor_map/sensor.py +++ b/esphome/components/binary_sensor_map/sensor.py @@ -39,7 +39,7 @@ CONFIG_SCHEMA = cv.typed_schema( ).extend( { cv.Required(CONF_CHANNELS): cv.All( - cv.ensure_list(entry), cv.Length(min=1) + cv.ensure_list(entry), cv.Length(min=1, max=64) ), } ), @@ -50,7 +50,7 @@ CONFIG_SCHEMA = cv.typed_schema( ).extend( { cv.Required(CONF_CHANNELS): cv.All( - cv.ensure_list(entry), cv.Length(min=1) + cv.ensure_list(entry), cv.Length(min=1, max=64) ), } ), diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 55fabf05ef..76950c944e 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -306,6 +306,13 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest api::global_api_server->send_bluetooth_device_unpairing(msg.address, ret == ESP_OK, ret); break; } + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: { + esp_bd_addr_t address; + uint64_to_bd_addr(msg.address, address); + esp_err_t ret = esp_ble_gattc_cache_clean(address); + api::global_api_server->send_bluetooth_device_clear_cache(msg.address, ret == ESP_OK, ret); + break; + } } } diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index b99e9a8527..a582abc8a3 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -68,6 +68,14 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +// Version 1: Initial version without active connections +// Version 2: Support for active connections +// Version 3: New connection API +// Version 4: Pairing support +// Version 5: Cache clear support +static const uint32_t ACTIVE_CONNECTIONS_VERSION = 5; +static const uint32_t PASSIVE_ONLY_VERSION = 1; + } // namespace bluetooth_proxy } // namespace esphome diff --git a/esphome/components/bme680/bme680.cpp b/esphome/components/bme680/bme680.cpp index e5704a8f9f..2b48f39e31 100644 --- a/esphome/components/bme680/bme680.cpp +++ b/esphome/components/bme680/bme680.cpp @@ -1,6 +1,6 @@ #include "bme680.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/log.h" namespace esphome { namespace bme680 { @@ -275,8 +275,8 @@ uint8_t BME680Component::calc_heater_resistance_(uint16_t temperature) { var3 = var1 + (var2 / 2); var4 = (var3 / (res_heat_range + 4)); var5 = (131 * res_heat_val) + 65536; - heatr_res_x100 = (int32_t)(((var4 / var5) - 250) * 34); - heatr_res = (uint8_t)((heatr_res_x100 + 50) / 100); + heatr_res_x100 = (int32_t) (((var4 / var5) - 250) * 34); + heatr_res = (uint8_t) ((heatr_res_x100 + 50) / 100); return heatr_res; } @@ -316,7 +316,7 @@ void BME680Component::read_data_() { uint32_t raw_temperature = (uint32_t(data[5]) << 12) | (uint32_t(data[6]) << 4) | (uint32_t(data[7]) >> 4); uint32_t raw_pressure = (uint32_t(data[2]) << 12) | (uint32_t(data[3]) << 4) | (uint32_t(data[4]) >> 4); uint32_t raw_humidity = (uint32_t(data[8]) << 8) | uint32_t(data[9]); - uint16_t raw_gas = (uint16_t)((uint32_t) data[13] * 4 | (((uint32_t) data[14]) / 64)); + uint16_t raw_gas = (uint16_t) ((uint32_t) data[13] * 4 | (((uint32_t) data[14]) / 64)); uint8_t gas_range = data[14] & 0x0F; float temperature = this->calc_temperature_(raw_temperature); diff --git a/esphome/components/bme680_bsec/__init__.py b/esphome/components/bme680_bsec/__init__.py index c9813c4974..085d2a574b 100644 --- a/esphome/components/bme680_bsec/__init__.py +++ b/esphome/components/bme680_bsec/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import i2c +from esphome.components import i2c, esp32 from esphome.const import CONF_ID CODEOWNERS = ["@trvrnrth"] @@ -32,22 +32,31 @@ BME680BSECComponent = bme680_bsec_ns.class_( "BME680BSECComponent", cg.Component, i2c.I2CDevice ) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(BME680BSECComponent), - cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature, - cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum( - IAQ_MODE_OPTIONS, upper=True - ), - cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum( - SAMPLE_RATE_OPTIONS, upper=True - ), - cv.Optional( - CONF_STATE_SAVE_INTERVAL, default="6hours" - ): cv.positive_time_period_minutes, - }, +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BME680BSECComponent), + cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature, + cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum( + IAQ_MODE_OPTIONS, upper=True + ), + cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum( + SAMPLE_RATE_OPTIONS, upper=True + ), + cv.Optional( + CONF_STATE_SAVE_INTERVAL, default="6hours" + ): cv.positive_time_period_minutes, + } + ).extend(i2c.i2c_device_schema(0x76)), cv.only_with_arduino, -).extend(i2c.i2c_device_schema(0x76)) + cv.Any( + cv.only_on_esp8266, + cv.All( + cv.only_on_esp32, + esp32.only_on_variant(supported=[esp32.const.VARIANT_ESP32]), + ), + ), +) async def to_code(config): diff --git a/esphome/components/button/button.cpp b/esphome/components/button/button.cpp index 3d58780d6d..dfa417de7b 100644 --- a/esphome/components/button/button.cpp +++ b/esphome/components/button/button.cpp @@ -6,9 +6,6 @@ namespace button { static const char *const TAG = "button"; -Button::Button(const std::string &name) : EntityBase(name) {} -Button::Button() : Button("") {} - void Button::press() { ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str()); this->press_action(); diff --git a/esphome/components/button/button.h b/esphome/components/button/button.h index 3b5f338887..a4902810b2 100644 --- a/esphome/components/button/button.h +++ b/esphome/components/button/button.h @@ -28,9 +28,6 @@ namespace button { */ class Button : public EntityBase { public: - explicit Button(); - explicit Button(const std::string &name); - /** Press this button. This is called by the front-end. * * For implementing buttons, please override press_action. diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index 5c60989afa..f1dadf673a 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -145,8 +145,8 @@ void CCS811Component::send_env_data_() { // https://github.com/adafruit/Adafruit_CCS811/blob/0990f5c620354d8bc087c4706bec091d8e6e5dfd/Adafruit_CCS811.cpp#L135-L142 uint16_t hum_conv = static_cast(lroundf(humidity * 512.0f + 0.5f)); uint16_t temp_conv = static_cast(lroundf(temperature * 512.0f + 0.5f)); - this->write_bytes(0x05, {(uint8_t)((hum_conv >> 8) & 0xff), (uint8_t)((hum_conv & 0xff)), - (uint8_t)((temp_conv >> 8) & 0xff), (uint8_t)((temp_conv & 0xff))}); + this->write_bytes(0x05, {(uint8_t) ((hum_conv >> 8) & 0xff), (uint8_t) ((hum_conv & 0xff)), + (uint8_t) ((temp_conv >> 8) & 0xff), (uint8_t) ((temp_conv & 0xff))}); } void CCS811Component::dump_config() { ESP_LOGCONFIG(TAG, "CCS811"); diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 37572ae913..b4d5ee9685 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -453,12 +453,7 @@ void Climate::set_visual_temperature_step_override(float target, float current) this->visual_target_temperature_step_override_ = target; this->visual_current_temperature_step_override_ = current; } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -Climate::Climate(const std::string &name) : EntityBase(name) {} -#pragma GCC diagnostic pop -Climate::Climate() : Climate("") {} ClimateCall Climate::make_call() { return ClimateCall(this); } ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 520036f718..43bd71657d 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -166,11 +166,6 @@ struct ClimateDeviceRestoreState { */ class Climate : public EntityBase { public: - /// Construct a climate device with empty name (will be set later). - Climate(); - /// Construct a climate device with a name. - Climate(const std::string &name); - /// The active mode of the climate device. ClimateMode mode{CLIMATE_MODE_OFF}; /// The active state of the climate device. diff --git a/esphome/components/copy/select/__init__.py b/esphome/components/copy/select/__init__.py index 7d4c1c7705..6254ed03e1 100644 --- a/esphome/components/copy/select/__init__.py +++ b/esphome/components/copy/select/__init__.py @@ -14,12 +14,15 @@ from .. import copy_ns CopySelect = copy_ns.class_("CopySelect", select.Select, cg.Component) -CONFIG_SCHEMA = select.SELECT_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(CopySelect), - cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + select.select_schema(CopySelect) + .extend( + { + cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select), + } + ) + .extend(cv.COMPONENT_SCHEMA) +) FINAL_VALIDATE_SCHEMA = cv.All( inherit_property_from(CONF_ICON, CONF_SOURCE_ID), diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp index 3d3abffae2..24dd88b698 100644 --- a/esphome/components/cover/cover.cpp +++ b/esphome/components/cover/cover.cpp @@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) { } } -Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {} +Cover::Cover() : position{COVER_OPEN} {} CoverCall::CoverCall(Cover *parent) : parent_(parent) {} CoverCall &CoverCall::set_command(const char *command) { @@ -204,18 +204,13 @@ optional Cover::restore_state_() { return {}; return recovered; } -Cover::Cover() : Cover("") {} std::string Cover::get_device_class() { if (this->device_class_override_.has_value()) return *this->device_class_override_; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - return this->device_class(); -#pragma GCC diagnostic pop + return ""; } bool Cover::is_fully_open() const { return this->position == COVER_OPEN; } bool Cover::is_fully_closed() const { return this->position == COVER_CLOSED; } -std::string Cover::device_class() { return ""; } CoverCall CoverRestoreState::to_call(Cover *cover) { auto call = cover->make_call(); diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index 27a821215a..c6a420fa97 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -111,7 +111,6 @@ const char *cover_operation_to_str(CoverOperation op); class Cover : public EntityBase { public: explicit Cover(); - explicit Cover(const std::string &name); /// The current operation of the cover (idle, opening, closing). CoverOperation current_operation{COVER_OPERATION_IDLE}; @@ -170,12 +169,6 @@ class Cover : public EntityBase { virtual void control(const CoverCall &call) = 0; - /** Override this to set the default device class. - * - * @deprecated This method is deprecated, set the property during config validation instead. (2022.1) - */ - virtual std::string device_class(); - optional restore_state_(); CallbackManager state_callback_{}; diff --git a/esphome/components/cs5460a/cs5460a.cpp b/esphome/components/cs5460a/cs5460a.cpp index b0c0531936..fb8e50b87a 100644 --- a/esphome/components/cs5460a/cs5460a.cpp +++ b/esphome/components/cs5460a/cs5460a.cpp @@ -305,7 +305,7 @@ bool CS5460AComponent::check_status_() { voltage_sensor_->publish_state(raw_voltage * voltage_multiplier_); if (power_sensor_ != nullptr && raw_energy != prev_raw_energy_) { - int32_t raw = (int32_t)(raw_energy << 8) >> 8; /* Sign-extend */ + int32_t raw = (int32_t) (raw_energy << 8) >> 8; /* Sign-extend */ power_sensor_->publish_state(raw * power_multiplier_); prev_raw_energy_ = raw_energy; } diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.cpp b/esphome/components/ct_clamp/ct_clamp_sensor.cpp index 51b0f1318c..d555befcde 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.cpp +++ b/esphome/components/ct_clamp/ct_clamp_sensor.cpp @@ -33,7 +33,10 @@ void CTClampSensor::update() { const float rms_ac_dc_squared = this->sample_squared_sum_ / this->num_samples_; const float rms_dc = this->sample_sum_ / this->num_samples_; - const float rms_ac = std::sqrt(rms_ac_dc_squared - rms_dc * rms_dc); + const float rms_ac_squared = rms_ac_dc_squared - rms_dc * rms_dc; + float rms_ac = 0; + if (rms_ac_squared > 0) + rms_ac = std::sqrt(rms_ac_squared); ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %d different samples (%d SPS)", this->name_.c_str(), rms_ac, this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_); this->publish_state(rms_ac); diff --git a/esphome/components/daly_bms/daly_bms.cpp b/esphome/components/daly_bms/daly_bms.cpp index 36047e02d0..3b41723327 100644 --- a/esphome/components/daly_bms/daly_bms.cpp +++ b/esphome/components/daly_bms/daly_bms.cpp @@ -61,8 +61,8 @@ void DalyBmsComponent::request_data_(uint8_t data_id) { request_message[9] = 0x00; // | request_message[10] = 0x00; // | request_message[11] = 0x00; // Empty Data - request_message[12] = (uint8_t)(request_message[0] + request_message[1] + request_message[2] + - request_message[3]); // Checksum (Lower byte of the other bytes sum) + request_message[12] = (uint8_t) (request_message[0] + request_message[1] + request_message[2] + + request_message[3]); // Checksum (Lower byte of the other bytes sum) this->write_array(request_message, sizeof(request_message)); this->flush(); diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index 223c3c8df1..c18baa1cca 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -1,15 +1,11 @@ import esphome.codegen as cg import esphome.config_validation as cv -import esphome.final_validate as fv -from esphome.components import logger from esphome.const import ( CONF_BLOCK, CONF_DEVICE, CONF_FRAGMENTATION, CONF_FREE, CONF_ID, - CONF_LEVEL, - CONF_LOGGER, CONF_LOOP_TIME, ) @@ -43,18 +39,6 @@ CONFIG_SCHEMA = cv.Schema( ).extend(cv.polling_component_schema("60s")) -def _final_validate(_): - logger_conf = fv.full_config.get()[CONF_LOGGER] - severity = logger.LOG_LEVEL_SEVERITY.index(logger_conf[CONF_LEVEL]) - if severity < logger.LOG_LEVEL_SEVERITY.index("DEBUG"): - raise cv.Invalid( - "The debug component requires the logger to be at least at DEBUG level" - ) - - -FINAL_VALIDATE_SCHEMA = _final_validate - - async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index c1ede684e6..9843fa1c99 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -37,6 +37,10 @@ static uint32_t get_free_heap() { } void DebugComponent::dump_config() { +#ifndef ESPHOME_LOG_HAS_DEBUG + return; // Can't log below if debug logging is disabled +#endif + std::string device_info; std::string reset_reason; device_info.reserve(256); diff --git a/esphome/components/dfplayer/dfplayer.cpp b/esphome/components/dfplayer/dfplayer.cpp index dcba95e20c..e16479570f 100644 --- a/esphome/components/dfplayer/dfplayer.cpp +++ b/esphome/components/dfplayer/dfplayer.cpp @@ -19,7 +19,7 @@ void DFPlayer::play_folder(uint16_t folder, uint16_t file) { } void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) { - uint8_t buffer[10]{0x7e, 0xff, 0x06, cmd, 0x01, (uint8_t)(argument >> 8), (uint8_t) argument, 0x00, 0x00, 0xef}; + uint8_t buffer[10]{0x7e, 0xff, 0x06, cmd, 0x01, (uint8_t) (argument >> 8), (uint8_t) argument, 0x00, 0x00, 0xef}; uint16_t checksum = 0; for (uint8_t i = 1; i < 7; i++) checksum += buffer[i]; diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 7cd85dabd4..19751e7355 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -664,7 +664,7 @@ bool Animation::get_pixel(int x, int y) const { return false; const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; const uint32_t frame_index = this->height_ * width_8 * this->current_frame_; - if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_)) + if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_)) return false; const uint32_t pos = x + y * width_8 + frame_index; return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); @@ -673,7 +673,7 @@ Color Animation::get_color_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return Color::BLACK; const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_; - if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_)) + if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_)) return Color::BLACK; const uint32_t pos = (x + y * this->width_ + frame_index) * 3; const uint32_t color32 = (progmem_read_byte(this->data_start_ + pos + 2) << 0) | @@ -685,7 +685,7 @@ Color Animation::get_rgb565_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return Color::BLACK; const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_; - if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_)) + if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_)) return Color::BLACK; const uint32_t pos = (x + y * this->width_ + frame_index) * 2; uint16_t rgb565 = @@ -699,7 +699,7 @@ Color Animation::get_grayscale_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return Color::BLACK; const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_; - if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_)) + if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_)) return Color::BLACK; const uint32_t pos = (x + y * this->width_ + frame_index); const uint8_t gray = progmem_read_byte(this->data_start_ + pos); diff --git a/esphome/components/ens210/ens210.cpp b/esphome/components/ens210/ens210.cpp index a9c519856b..86890c05e8 100644 --- a/esphome/components/ens210/ens210.cpp +++ b/esphome/components/ens210/ens210.cpp @@ -168,7 +168,7 @@ void ENS210Component::update() { return; } // Pack bytes for humidity - h_val_data = (uint32_t)((uint32_t) data[5] << 16 | (uint32_t) data[4] << 8 | (uint32_t) data[3]); + h_val_data = (uint32_t) ((uint32_t) data[5] << 16 | (uint32_t) data[4] << 8 | (uint32_t) data[3]); // Extract humidity data and update the status extract_measurement_(h_val_data, &humidity_data, &humidity_status); @@ -183,7 +183,7 @@ void ENS210Component::update() { return; } // Pack bytes for temperature - t_val_data = (uint32_t)((uint32_t) data[2] << 16 | (uint32_t) data[1] << 8 | (uint32_t) data[0]); + t_val_data = (uint32_t) ((uint32_t) data[2] << 16 | (uint32_t) data[1] << 8 | (uint32_t) data[0]); // Extract temperature data and update the status extract_measurement_(t_val_data, &temperature_data, &temperature_status); diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 62021afea9..d0f74b7226 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -163,7 +163,7 @@ RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5) # The platformio/espressif32 version to use for arduino frameworks # - https://github.com/platformio/platform-espressif32/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 -ARDUINO_PLATFORM_VERSION = cv.Version(5, 2, 0) +ARDUINO_PLATFORM_VERSION = cv.Version(5, 3, 0) # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index 6123d83a34..b47392bc6b 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -23,7 +23,7 @@ void loop(); namespace esphome { void IRAM_ATTR HOT yield() { vPortYield(); } -uint32_t IRAM_ATTR HOT millis() { return (uint32_t)(esp_timer_get_time() / 1000ULL); } +uint32_t IRAM_ATTR HOT millis() { return (uint32_t) (esp_timer_get_time() / 1000ULL); } void IRAM_ATTR HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); } void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); } diff --git a/esphome/components/esp32/gpio.cpp b/esphome/components/esp32/gpio.cpp index aafdf80726..7896597d3e 100644 --- a/esphome/components/esp32/gpio.cpp +++ b/esphome/components/esp32/gpio.cpp @@ -2,6 +2,7 @@ #include "gpio.h" #include "esphome/core/log.h" +#include namespace esphome { namespace esp32 { @@ -74,7 +75,7 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi std::string ESP32InternalGPIOPin::dump_summary() const { char buffer[32]; - snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast(pin_)); + snprintf(buffer, sizeof(buffer), "GPIO%" PRIu32, static_cast(pin_)); return buffer; } diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index 6a6305cf87..f90b8a4603 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -5,6 +5,7 @@ #include "esphome/core/log.h" #include #include +#include #include #include @@ -101,7 +102,7 @@ class ESP32Preferences : public ESPPreferences { pref->nvs_handle = nvs_handle; uint32_t keyval = type; - pref->key = str_sprintf("%u", keyval); + pref->key = str_sprintf("%" PRIu32, keyval); return ESPPreferenceObject(pref); } diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 160084b913..502399f97a 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -211,7 +212,16 @@ void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; } -void ESP32BLE::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE:"); } +void ESP32BLE::dump_config() { + const uint8_t *mac_address = esp_bt_dev_get_address(); + if (mac_address) { + ESP_LOGCONFIG(TAG, "ESP32 BLE:"); + ESP_LOGCONFIG(TAG, " MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2], + mac_address[3], mac_address[4], mac_address[5]); + } else { + ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled"); + } +} ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 9ca82c7239..40eff49266 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -316,18 +316,18 @@ float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) { case 0xD: // int12. case 0xE: // int16. if (length > 2) { - return (float) ((int16_t)(value[1] << 8) + (int16_t) value[2]); + return (float) ((int16_t) (value[1] << 8) + (int16_t) value[2]); } // fall through case 0xF: // int24. if (length > 3) { - return (float) ((int32_t)(value[1] << 16) + (int32_t)(value[2] << 8) + (int32_t)(value[3])); + return (float) ((int32_t) (value[1] << 16) + (int32_t) (value[2] << 8) + (int32_t) (value[3])); } // fall through case 0x10: // int32. if (length > 4) { - return (float) ((int32_t)(value[1] << 24) + (int32_t)(value[2] << 16) + (int32_t)(value[3] << 8) + - (int32_t)(value[4])); + return (float) ((int32_t) (value[1] << 24) + (int32_t) (value[2] << 16) + (int32_t) (value[3] << 8) + + (int32_t) (value[4])); } } ESP_LOGW(TAG, "[%d] [%s] Cannot parse characteristic value of type 0x%x length %d", this->connection_index_, diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index 2879da4d8c..97886d0b19 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -45,10 +45,11 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { memset(this->remote_bda_, 0, sizeof(this->remote_bda_)); this->address_str_ = ""; } else { - this->address_str_ = str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, (uint8_t)(this->address_ >> 40) & 0xff, - (uint8_t)(this->address_ >> 32) & 0xff, (uint8_t)(this->address_ >> 24) & 0xff, - (uint8_t)(this->address_ >> 16) & 0xff, (uint8_t)(this->address_ >> 8) & 0xff, - (uint8_t)(this->address_ >> 0) & 0xff); + this->address_str_ = + str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, (uint8_t) (this->address_ >> 40) & 0xff, + (uint8_t) (this->address_ >> 32) & 0xff, (uint8_t) (this->address_ >> 24) & 0xff, + (uint8_t) (this->address_ >> 16) & 0xff, (uint8_t) (this->address_ >> 8) & 0xff, + (uint8_t) (this->address_ >> 0) & 0xff); } } std::string address_str() const { return this->address_str_; } diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index df822ac0b9..15a51f6ede 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -148,44 +148,44 @@ bool BLECharacteristic::is_failed() { void BLECharacteristic::set_broadcast_property(bool value) { if (value) { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_BROADCAST); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_BROADCAST); } else { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST); } } void BLECharacteristic::set_indicate_property(bool value) { if (value) { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_INDICATE); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_INDICATE); } else { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_INDICATE); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_INDICATE); } } void BLECharacteristic::set_notify_property(bool value) { if (value) { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_NOTIFY); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_NOTIFY); } else { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY); } } void BLECharacteristic::set_read_property(bool value) { if (value) { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_READ); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_READ); } else { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_READ); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_READ); } } void BLECharacteristic::set_write_property(bool value) { if (value) { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE); } else { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE); } } void BLECharacteristic::set_write_no_response_property(bool value) { if (value) { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR); } else { - this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR); + this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR); } } diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index b3abbd5c13..4cbdf7ca5c 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -55,6 +55,22 @@ FRAME_SIZES = { "SXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024, "1600X1200": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200, "UXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200, + "1920X1080": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1920X1080, + "FHD": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1920X1080, + "720X1280": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_720X1280, + "PHD": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_720X1280, + "864X1536": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_864X1536, + "P3MP": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_864X1536, + "2048X1536": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2048X1536, + "QXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2048X1536, + "2560X1440": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1440, + "QHD": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1440, + "2560X1600": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1600, + "WQXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1600, + "1080X1920": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1080X1920, + "PFHD": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1080X1920, + "2560X1920": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1920, + "QSXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1920, } ESP32GainControlMode = esp32_camera_ns.enum("ESP32GainControlMode") ENUM_GAIN_CONTROL_MODE = { @@ -140,7 +156,7 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( { cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( - cv.frequency, cv.one_of(20e6, 10e6) + cv.frequency, cv.Range(min=8e6, max=20e6) ), } ), diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index b1bf1d8532..e4020a902e 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -91,6 +91,30 @@ void ESP32Camera::dump_config() { case FRAMESIZE_UXGA: ESP_LOGCONFIG(TAG, " Resolution: 1600x1200 (UXGA)"); break; + case FRAMESIZE_FHD: + ESP_LOGCONFIG(TAG, " Resolution: 1920x1080 (FHD)"); + break; + case FRAMESIZE_P_HD: + ESP_LOGCONFIG(TAG, " Resolution: 720x1280 (P_HD)"); + break; + case FRAMESIZE_P_3MP: + ESP_LOGCONFIG(TAG, " Resolution: 864x1536 (P_3MP)"); + break; + case FRAMESIZE_QXGA: + ESP_LOGCONFIG(TAG, " Resolution: 2048x1536 (QXGA)"); + break; + case FRAMESIZE_QHD: + ESP_LOGCONFIG(TAG, " Resolution: 2560x1440 (QHD)"); + break; + case FRAMESIZE_WQXGA: + ESP_LOGCONFIG(TAG, " Resolution: 2560x1600 (WQXGA)"); + break; + case FRAMESIZE_P_FHD: + ESP_LOGCONFIG(TAG, " Resolution: 1080x1920 (P_FHD)"); + break; + case FRAMESIZE_QSXGA: + ESP_LOGCONFIG(TAG, " Resolution: 2560x1920 (QSXGA)"); + break; default: break; } @@ -178,7 +202,7 @@ void ESP32Camera::loop() { float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; } /* ---------------- constructors ---------------- */ -ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) { +ESP32Camera::ESP32Camera() { this->config_.pin_pwdn = -1; this->config_.pin_reset = -1; this->config_.pin_xclk = -1; @@ -191,7 +215,6 @@ ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) { global_esp32_camera = this; } -ESP32Camera::ESP32Camera() : ESP32Camera("") {} /* ---------------- setters ---------------- */ /* set pin assignment */ @@ -257,6 +280,30 @@ void ESP32Camera::set_frame_size(ESP32CameraFrameSize size) { case ESP32_CAMERA_SIZE_1600X1200: this->config_.frame_size = FRAMESIZE_UXGA; break; + case ESP32_CAMERA_SIZE_1920X1080: + this->config_.frame_size = FRAMESIZE_FHD; + break; + case ESP32_CAMERA_SIZE_720X1280: + this->config_.frame_size = FRAMESIZE_P_HD; + break; + case ESP32_CAMERA_SIZE_864X1536: + this->config_.frame_size = FRAMESIZE_P_3MP; + break; + case ESP32_CAMERA_SIZE_2048X1536: + this->config_.frame_size = FRAMESIZE_QXGA; + break; + case ESP32_CAMERA_SIZE_2560X1440: + this->config_.frame_size = FRAMESIZE_QHD; + break; + case ESP32_CAMERA_SIZE_2560X1600: + this->config_.frame_size = FRAMESIZE_WQXGA; + break; + case ESP32_CAMERA_SIZE_1080X1920: + this->config_.frame_size = FRAMESIZE_P_FHD; + break; + case ESP32_CAMERA_SIZE_2560X1920: + this->config_.frame_size = FRAMESIZE_QSXGA; + break; } } void ESP32Camera::set_jpeg_quality(uint8_t quality) { this->config_.jpeg_quality = quality; } diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index 87c5b0ba4a..5f88c6fda8 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -29,6 +29,14 @@ enum ESP32CameraFrameSize { ESP32_CAMERA_SIZE_1024X768, // XGA ESP32_CAMERA_SIZE_1280X1024, // SXGA ESP32_CAMERA_SIZE_1600X1200, // UXGA + ESP32_CAMERA_SIZE_1920X1080, // FHD + ESP32_CAMERA_SIZE_720X1280, // PHD + ESP32_CAMERA_SIZE_864X1536, // P3MP + ESP32_CAMERA_SIZE_2048X1536, // QXGA + ESP32_CAMERA_SIZE_2560X1440, // QHD + ESP32_CAMERA_SIZE_2560X1600, // WQXGA + ESP32_CAMERA_SIZE_1080X1920, // PFHD + ESP32_CAMERA_SIZE_2560X1920, // QSXGA }; enum ESP32AgcGainCeiling { @@ -95,7 +103,6 @@ class CameraImageReader { /* ---------------- ESP32Camera class ---------------- */ class ESP32Camera : public Component, public EntityBase { public: - ESP32Camera(const std::string &name); ESP32Camera(); /* setters */ diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index 7170a6dabf..ae7f0b6427 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -22,20 +22,12 @@ ESP32ImprovComponent = esp32_improv_ns.class_( ) -def validate_none_(value): - if value in ("none", "None"): - return None - if cv.boolean(value) is False: - return None - raise cv.Invalid("Must be none") - - 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( - validate_none_, cv.use_id(binary_sensor.BinarySensor) + cv.none, cv.use_id(binary_sensor.BinarySensor) ), cv.Optional(CONF_STATUS_INDICATOR): cv.use_id(output.BinaryOutput), cv.Optional( diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index f6ca376681..bedc0a4c30 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -34,6 +34,7 @@ ETHERNET_TYPES = { "DP83848": EthernetType.ETHERNET_TYPE_DP83848, "IP101": EthernetType.ETHERNET_TYPE_IP101, "JL1101": EthernetType.ETHERNET_TYPE_JL1101, + "KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081, } emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t") diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 7120223cc9..0487ea5498 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -26,8 +26,10 @@ EthernetComponent::EthernetComponent() { global_eth_component = this; } void EthernetComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Ethernet..."); - // Delay here to allow power to stabilise before Ethernet is initialised. - delay(300); // NOLINT + if (esp_reset_reason() != ESP_RST_DEEPSLEEP) { + // Delay here to allow power to stabilise before Ethernet is initialized. + delay(300); // NOLINT + } esp_err_t err; err = esp_netif_init(); @@ -52,26 +54,29 @@ void EthernetComponent::setup() { esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); - esp_eth_phy_t *phy; switch (this->type_) { case ETHERNET_TYPE_LAN8720: { - phy = esp_eth_phy_new_lan87xx(&phy_config); + this->phy_ = esp_eth_phy_new_lan87xx(&phy_config); break; } case ETHERNET_TYPE_RTL8201: { - phy = esp_eth_phy_new_rtl8201(&phy_config); + this->phy_ = esp_eth_phy_new_rtl8201(&phy_config); break; } case ETHERNET_TYPE_DP83848: { - phy = esp_eth_phy_new_dp83848(&phy_config); + this->phy_ = esp_eth_phy_new_dp83848(&phy_config); break; } case ETHERNET_TYPE_IP101: { - phy = esp_eth_phy_new_ip101(&phy_config); + this->phy_ = esp_eth_phy_new_ip101(&phy_config); break; } case ETHERNET_TYPE_JL1101: { - phy = esp_eth_phy_new_jl1101(&phy_config); + this->phy_ = esp_eth_phy_new_jl1101(&phy_config); + break; + } + case ETHERNET_TYPE_KSZ8081: { + this->phy_ = esp_eth_phy_new_ksz8081(&phy_config); break; } default: { @@ -80,7 +85,7 @@ void EthernetComponent::setup() { } } - esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, this->phy_); this->eth_handle_ = nullptr; err = esp_eth_driver_install(ð_config, &this->eth_handle_); ESPHL_ERROR_CHECK(err, "ETH driver install error"); @@ -140,7 +145,7 @@ void EthernetComponent::loop() { } void EthernetComponent::dump_config() { - std::string eth_type; + const char *eth_type; switch (this->type_) { case ETHERNET_TYPE_LAN8720: eth_type = "LAN8720"; @@ -158,6 +163,14 @@ void EthernetComponent::dump_config() { eth_type = "IP101"; break; + case ETHERNET_TYPE_JL1101: + eth_type = "JL1101"; + break; + + case ETHERNET_TYPE_KSZ8081: + eth_type = "KSZ8081"; + break; + default: eth_type = "Unknown"; break; @@ -170,7 +183,8 @@ void EthernetComponent::dump_config() { } ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_); ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_); - ESP_LOGCONFIG(TAG, " Type: %s", eth_type.c_str()); + ESP_LOGCONFIG(TAG, " Type: %s", eth_type); + ESP_LOGCONFIG(TAG, " PHY addr: %u", this->phy_addr_); } float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; } @@ -255,14 +269,22 @@ void EthernetComponent::start_connect_() { if (this->manual_ip_.has_value()) { if (uint32_t(this->manual_ip_->dns1) != 0) { ip_addr_t d; +#if LWIP_IPV6 d.type = IPADDR_TYPE_V4; d.u_addr.ip4.addr = static_cast(this->manual_ip_->dns1); +#else + d.addr = static_cast(this->manual_ip_->dns1); +#endif dns_setserver(0, &d); } - if (uint32_t(this->manual_ip_->dns1) != 0) { + if (uint32_t(this->manual_ip_->dns2) != 0) { ip_addr_t d; +#if LWIP_IPV6 d.type = IPADDR_TYPE_V4; d.u_addr.ip4.addr = static_cast(this->manual_ip_->dns2); +#else + d.addr = static_cast(this->manual_ip_->dns2); +#endif dns_setserver(1, &d); } } else { @@ -289,8 +311,13 @@ void EthernetComponent::dump_connect_params_() { const ip_addr_t *dns_ip1 = dns_getserver(0); const ip_addr_t *dns_ip2 = dns_getserver(1); +#if LWIP_IPV6 ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str()); ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str()); +#else + ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->addr).str().c_str()); + ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str()); +#endif esp_err_t err; @@ -330,6 +357,21 @@ std::string EthernetComponent::get_use_address() const { void EthernetComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } +bool EthernetComponent::powerdown() { + ESP_LOGI(TAG, "Powering down ethernet PHY"); + if (this->phy_ == nullptr) { + ESP_LOGE(TAG, "Ethernet PHY not assigned"); + return false; + } + this->connected_ = false; + this->started_ = false; + if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) { + ESP_LOGE(TAG, "Error powering down ethernet PHY"); + return false; + } + return true; +} + } // namespace ethernet } // namespace esphome diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 872ed17044..918e47212f 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -14,11 +14,13 @@ namespace esphome { namespace ethernet { enum EthernetType { - ETHERNET_TYPE_LAN8720 = 0, + ETHERNET_TYPE_UNKNOWN = 0, + ETHERNET_TYPE_LAN8720, ETHERNET_TYPE_RTL8201, ETHERNET_TYPE_DP83848, ETHERNET_TYPE_IP101, ETHERNET_TYPE_JL1101, + ETHERNET_TYPE_KSZ8081, }; struct ManualIP { @@ -43,6 +45,7 @@ class EthernetComponent : public Component { void dump_config() override; float get_setup_priority() const override; bool can_proceed() override; + void on_shutdown() override { powerdown(); } bool is_connected(); void set_phy_addr(uint8_t phy_addr); @@ -56,6 +59,7 @@ class EthernetComponent : public Component { network::IPAddress get_ip_address(); std::string get_use_address() const; void set_use_address(const std::string &use_address); + bool powerdown(); protected: static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); @@ -69,7 +73,7 @@ class EthernetComponent : public Component { int power_pin_{-1}; uint8_t mdc_pin_{23}; uint8_t mdio_pin_{18}; - EthernetType type_{ETHERNET_TYPE_LAN8720}; + EthernetType type_{ETHERNET_TYPE_UNKNOWN}; emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN}; emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO}; optional manual_ip_{}; @@ -80,6 +84,7 @@ class EthernetComponent : public Component { uint32_t connect_begin_; esp_netif_t *eth_netif_{nullptr}; esp_eth_handle_t eth_handle_; + esp_eth_phy_t *phy_{nullptr}; }; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/ezo/ezo.h b/esphome/components/ezo/ezo.h index 72e82a574f..28b46643e9 100644 --- a/esphome/components/ezo/ezo.h +++ b/esphome/components/ezo/ezo.h @@ -12,14 +12,14 @@ static const char *const TAG = "ezo.sensor"; enum EzoCommandType : uint8_t { EZO_READ = 0, - EZO_LED = 1, - EZO_DEVICE_INFORMATION = 2, - EZO_SLOPE = 3, + EZO_LED, + EZO_DEVICE_INFORMATION, + EZO_SLOPE, EZO_CALIBRATION, - EZO_SLEEP = 4, - EZO_I2C = 5, - EZO_T = 6, - EZO_CUSTOM = 7 + EZO_SLEEP, + EZO_I2C, + EZO_T, + EZO_CUSTOM }; enum EzoCalibrationType : uint8_t { EZO_CAL_LOW = 0, EZO_CAL_MID = 1, EZO_CAL_HIGH = 2 }; diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index eb67bbcbd7..9a05bff3a0 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -63,7 +63,7 @@ FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.temp FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.GenerateID(): cv.declare_id(Fan), - cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( + cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum( RESTORE_MODES, upper=True, space="_" ), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent), diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index c8a3064f6c..87566bad4a 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -80,9 +80,6 @@ void FanRestoreState::apply(Fan &fan) { fan.publish_state(); } -Fan::Fan() : EntityBase("") {} -Fan::Fan(const std::string &name) : EntityBase(name) {} - FanCall Fan::turn_on() { return this->make_call().set_state(true); } FanCall Fan::turn_off() { return this->make_call().set_state(false); } FanCall Fan::toggle() { return this->make_call().set_state(!this->state); } diff --git a/esphome/components/fan/fan.h b/esphome/components/fan/fan.h index 337bb3935a..f9d317e675 100644 --- a/esphome/components/fan/fan.h +++ b/esphome/components/fan/fan.h @@ -99,10 +99,6 @@ struct FanRestoreState { class Fan : public EntityBase { public: - Fan(); - /// Construct the fan with name. - explicit Fan(const std::string &name); - /// The current on/off state of the fan. bool state{false}; /// The current oscillation state of the fan. diff --git a/esphome/components/fan/fan_state.h b/esphome/components/fan/fan_state.h index 044ee59736..5926e700b0 100644 --- a/esphome/components/fan/fan_state.h +++ b/esphome/components/fan/fan_state.h @@ -15,7 +15,6 @@ enum ESPDEPRECATED("LegacyFanDirection members are deprecated, use FanDirection class ESPDEPRECATED("FanState is deprecated, use Fan instead.", "2022.2") FanState : public Fan, public Component { public: FanState() = default; - explicit FanState(const std::string &name) : Fan(name) {} /// Get the traits of this fan. FanTraits get_traits() override { return this->traits_; } diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index 1d6cb776b7..d27b0ca4cd 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -95,7 +95,7 @@ void FingerprintGrowComponent::scan_and_match_() { } if (this->scan_image_(1) == OK) { this->waiting_removal_ = true; - this->data_ = {SEARCH, 0x01, 0x00, 0x00, (uint8_t)(this->capacity_ >> 8), (uint8_t)(this->capacity_ & 0xFF)}; + this->data_ = {SEARCH, 0x01, 0x00, 0x00, (uint8_t) (this->capacity_ >> 8), (uint8_t) (this->capacity_ & 0xFF)}; switch (this->send_command_()) { case OK: { ESP_LOGD(TAG, "Fingerprint matched"); @@ -171,7 +171,7 @@ uint8_t FingerprintGrowComponent::save_fingerprint_() { } ESP_LOGI(TAG, "Storing model"); - this->data_ = {STORE, 0x01, (uint8_t)(this->enrollment_slot_ >> 8), (uint8_t)(this->enrollment_slot_ & 0xFF)}; + this->data_ = {STORE, 0x01, (uint8_t) (this->enrollment_slot_ >> 8), (uint8_t) (this->enrollment_slot_ & 0xFF)}; switch (this->send_command_()) { case OK: ESP_LOGI(TAG, "Stored model"); @@ -188,8 +188,8 @@ uint8_t FingerprintGrowComponent::save_fingerprint_() { bool FingerprintGrowComponent::check_password_() { ESP_LOGD(TAG, "Checking password"); - this->data_ = {VERIFY_PASSWORD, (uint8_t)(this->password_ >> 24), (uint8_t)(this->password_ >> 16), - (uint8_t)(this->password_ >> 8), (uint8_t)(this->password_ & 0xFF)}; + this->data_ = {VERIFY_PASSWORD, (uint8_t) (this->password_ >> 24), (uint8_t) (this->password_ >> 16), + (uint8_t) (this->password_ >> 8), (uint8_t) (this->password_ & 0xFF)}; switch (this->send_command_()) { case OK: ESP_LOGD(TAG, "Password verified"); @@ -203,8 +203,8 @@ bool FingerprintGrowComponent::check_password_() { bool FingerprintGrowComponent::set_password_() { ESP_LOGI(TAG, "Setting new password: %d", this->new_password_); - this->data_ = {SET_PASSWORD, (uint8_t)(this->new_password_ >> 24), (uint8_t)(this->new_password_ >> 16), - (uint8_t)(this->new_password_ >> 8), (uint8_t)(this->new_password_ & 0xFF)}; + this->data_ = {SET_PASSWORD, (uint8_t) (this->new_password_ >> 24), (uint8_t) (this->new_password_ >> 16), + (uint8_t) (this->new_password_ >> 8), (uint8_t) (this->new_password_ & 0xFF)}; if (this->send_command_() == OK) { ESP_LOGI(TAG, "New password successfully set"); ESP_LOGI(TAG, "Define the new password in your configuration and reflash now"); @@ -250,7 +250,7 @@ void FingerprintGrowComponent::get_fingerprint_count_() { void FingerprintGrowComponent::delete_fingerprint(uint16_t finger_id) { ESP_LOGI(TAG, "Deleting fingerprint in slot %d", finger_id); - this->data_ = {DELETE, (uint8_t)(finger_id >> 8), (uint8_t)(finger_id & 0xFF), 0x00, 0x01}; + this->data_ = {DELETE, (uint8_t) (finger_id >> 8), (uint8_t) (finger_id & 0xFF), 0x00, 0x01}; switch (this->send_command_()) { case OK: ESP_LOGI(TAG, "Deleted fingerprint"); @@ -320,8 +320,8 @@ void FingerprintGrowComponent::aura_led_control(uint8_t state, uint8_t speed, ui } uint8_t FingerprintGrowComponent::send_command_() { - this->write((uint8_t)(START_CODE >> 8)); - this->write((uint8_t)(START_CODE & 0xFF)); + this->write((uint8_t) (START_CODE >> 8)); + this->write((uint8_t) (START_CODE & 0xFF)); this->write(this->address_[0]); this->write(this->address_[1]); this->write(this->address_[2]); @@ -329,8 +329,8 @@ uint8_t FingerprintGrowComponent::send_command_() { this->write(COMMAND); uint16_t wire_length = this->data_.size() + 2; - this->write((uint8_t)(wire_length >> 8)); - this->write((uint8_t)(wire_length & 0xFF)); + this->write((uint8_t) (wire_length >> 8)); + this->write((uint8_t) (wire_length & 0xFF)); uint16_t sum = ((wire_length) >> 8) + ((wire_length) &0xFF) + COMMAND; for (auto data : this->data_) { @@ -338,8 +338,8 @@ uint8_t FingerprintGrowComponent::send_command_() { sum += data; } - this->write((uint8_t)(sum >> 8)); - this->write((uint8_t)(sum & 0xFF)); + this->write((uint8_t) (sum >> 8)); + this->write((uint8_t) (sum & 0xFF)); this->data_.clear(); @@ -354,11 +354,11 @@ uint8_t FingerprintGrowComponent::send_command_() { byte = this->read(); switch (idx) { case 0: - if (byte != (uint8_t)(START_CODE >> 8)) + if (byte != (uint8_t) (START_CODE >> 8)) continue; break; case 1: - if (byte != (uint8_t)(START_CODE & 0xFF)) { + if (byte != (uint8_t) (START_CODE & 0xFF)) { idx = 0; continue; } diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h index 96d02a1e8c..fd316237f7 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.h +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -91,10 +91,10 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic void dump_config() override; void set_address(uint32_t address) { - this->address_[0] = (uint8_t)(address >> 24); - this->address_[1] = (uint8_t)(address >> 16); - this->address_[2] = (uint8_t)(address >> 8); - this->address_[3] = (uint8_t)(address & 0xFF); + this->address_[0] = (uint8_t) (address >> 24); + this->address_[1] = (uint8_t) (address >> 16); + this->address_[2] = (uint8_t) (address >> 8); + this->address_[3] = (uint8_t) (address & 0xFF); } void set_sensing_pin(GPIOPin *sensing_pin) { this->sensing_pin_ = sensing_pin; } void set_password(uint32_t password) { this->password_ = password; } diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index daff89e0a6..c229f17dd8 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -122,11 +122,18 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo } // Adjust limits to nice y_per_div boundaries - int yn = int(ymin / y_per_div); - int ym = int(ymax / y_per_div) + int(1 * (fmodf(ymax, y_per_div) != 0)); - ymin = yn * y_per_div; - ymax = ym * y_per_div; - yrange = ymax - ymin; + int yn = 0; + int ym = 1; + if (!std::isnan(ymin) && !std::isnan(ymax)) { + yn = (int) floorf(ymin / y_per_div); + ym = (int) ceilf(ymax / y_per_div); + if (yn == ym) { + ym++; + } + ymin = yn * y_per_div; + ymax = ym * y_per_div; + yrange = ymax - ymin; + } /// Draw grid if (!std::isnan(this->gridspacing_y_)) { diff --git a/esphome/components/hitachi_ac344/hitachi_ac344.cpp b/esphome/components/hitachi_ac344/hitachi_ac344.cpp index 7b93b00503..2825e4f04c 100644 --- a/esphome/components/hitachi_ac344/hitachi_ac344.cpp +++ b/esphome/components/hitachi_ac344/hitachi_ac344.cpp @@ -12,7 +12,7 @@ void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, con uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); // Calculate the mask & clear the space for the data. // Clear the destination bits. - *dst &= ~(uint8_t)(mask << offset); + *dst &= ~(uint8_t) (mask << offset); // Merge in the data. *dst |= ((data & mask) << offset); } diff --git a/esphome/components/hitachi_ac424/hitachi_ac424.cpp b/esphome/components/hitachi_ac424/hitachi_ac424.cpp index 65cfaa4175..0bfc3ae564 100644 --- a/esphome/components/hitachi_ac424/hitachi_ac424.cpp +++ b/esphome/components/hitachi_ac424/hitachi_ac424.cpp @@ -12,7 +12,7 @@ void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, con uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); // Calculate the mask & clear the space for the data. // Clear the destination bits. - *dst &= ~(uint8_t)(mask << offset); + *dst &= ~(uint8_t) (mask << offset); // Merge in the data. *dst |= ((data & mask) << offset); } diff --git a/esphome/components/honeywellabp/honeywellabp.cpp b/esphome/components/honeywellabp/honeywellabp.cpp index 910c39e8c8..124bd6bb95 100644 --- a/esphome/components/honeywellabp/honeywellabp.cpp +++ b/esphome/components/honeywellabp/honeywellabp.cpp @@ -35,9 +35,9 @@ uint8_t HONEYWELLABPSensor::readsensor_() { // if device is normal and there is new data, bitmask and save the raw data if (status_ == 0) { // 14 - bit pressure is the last 6 bits of byte 0 (high bits) & all of byte 1 (lowest 8 bits) - pressure_count_ = ((uint16_t)(buf_[0]) << 8 & 0x3F00) | ((uint16_t)(buf_[1]) & 0xFF); + pressure_count_ = ((uint16_t) (buf_[0]) << 8 & 0x3F00) | ((uint16_t) (buf_[1]) & 0xFF); // 11 - bit temperature is all of byte 2 (lowest 8 bits) and the first three bits of byte 3 - temperature_count_ = (((uint16_t)(buf_[2]) << 3) & 0x7F8) | (((uint16_t)(buf_[3]) >> 5) & 0x7); + temperature_count_ = (((uint16_t) (buf_[2]) << 3) & 0x7F8) | (((uint16_t) (buf_[3]) >> 5) & 0x7); ESP_LOGV(TAG, "Sensor pressure_count_ %d", pressure_count_); ESP_LOGV(TAG, "Sensor temperature_count_ %d", temperature_count_); } diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index 16d89c3450..e08622a3ae 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -154,18 +154,25 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn } } uint8_t status = wire_->endTransmission(stop); - if (status == 0) { - return ERROR_OK; - } else if (status == 1) { - // transmit buffer not large enough - ESP_LOGVV(TAG, "TX failed: buffer not large enough"); - return ERROR_UNKNOWN; - } else if (status == 2 || status == 3) { - ESP_LOGVV(TAG, "TX failed: not acknowledged"); - return ERROR_NOT_ACKNOWLEDGED; + switch (status) { + case 0: + return ERROR_OK; + case 1: + // transmit buffer not large enough + ESP_LOGVV(TAG, "TX failed: buffer not large enough"); + return ERROR_UNKNOWN; + case 2: + case 3: + ESP_LOGVV(TAG, "TX failed: not acknowledged"); + return ERROR_NOT_ACKNOWLEDGED; + case 5: + ESP_LOGVV(TAG, "TX failed: timeout"); + return ERROR_UNKNOWN; + case 4: + default: + ESP_LOGVV(TAG, "TX failed: unknown error %u", status); + return ERROR_UNKNOWN; } - ESP_LOGVV(TAG, "TX failed: unknown error %u", status); - return ERROR_UNKNOWN; } /// Perform I2C bus recovery, see: diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index 1e2a7304f2..5178f6d4f2 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -5,6 +5,7 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" #include +#include namespace esphome { namespace i2c { @@ -47,7 +48,7 @@ void IDFI2CBus::dump_config() { ESP_LOGCONFIG(TAG, "I2C Bus:"); ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); - ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_); switch (this->recovery_result_) { case RECOVERY_COMPLETED: ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered"); diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index e69de29bb2..1773d3082f 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -0,0 +1,70 @@ +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 +from esphome.components.esp32 import get_esp32_variant +from esphome.components.esp32.const import ( + VARIANT_ESP32, + VARIANT_ESP32S2, + VARIANT_ESP32S3, + VARIANT_ESP32C3, +) + +CODEOWNERS = ["@jesserockz"] +DEPENDENCIES = ["esp32"] +MULTI_CONF = True + +CONF_I2S_DOUT_PIN = "i2s_dout_pin" +CONF_I2S_DIN_PIN = "i2s_din_pin" +CONF_I2S_BCLK_PIN = "i2s_bclk_pin" +CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin" + +CONF_I2S_AUDIO = "i2s_audio" +CONF_I2S_AUDIO_ID = "i2s_audio_id" + +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) +) + +# https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h +I2S_PORTS = { + VARIANT_ESP32: 2, + VARIANT_ESP32S2: 1, + VARIANT_ESP32S3: 2, + VARIANT_ESP32C3: 1, +} + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(I2SAudioComponent), + cv.Required(CONF_I2S_BCLK_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_I2S_LRCLK_PIN): pins.internal_gpio_output_pin_number, + } +) + + +def _final_validate(_): + i2s_audio_configs = fv.full_config.get()[CONF_I2S_AUDIO] + variant = get_esp32_variant() + if variant not in I2S_PORTS: + raise cv.Invalid(f"Unsupported variant {variant}") + if len(i2s_audio_configs) > I2S_PORTS[variant]: + raise cv.Invalid( + f"Only {I2S_PORTS[variant]} I2S audio ports are supported on {variant}" + ) + + +FINAL_VALIDATE_SCHEMA = _final_validate + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN])) + cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN])) diff --git a/esphome/components/i2s_audio/i2s_audio.cpp b/esphome/components/i2s_audio/i2s_audio.cpp new file mode 100644 index 0000000000..c1a608c064 --- /dev/null +++ b/esphome/components/i2s_audio/i2s_audio.cpp @@ -0,0 +1,30 @@ +#include "i2s_audio.h" + +#ifdef USE_ESP32 + +#include "esphome/core/log.h" + +namespace esphome { +namespace i2s_audio { + +static const char *const TAG = "i2s_audio"; + +void I2SAudioComponent::setup() { + static i2s_port_t next_port_num = I2S_NUM_0; + + if (next_port_num >= I2S_NUM_MAX) { + ESP_LOGE(TAG, "Too many I2S Audio components!"); + this->mark_failed(); + return; + } + + this->port_ = next_port_num; + next_port_num = (i2s_port_t) (next_port_num + 1); + + ESP_LOGCONFIG(TAG, "Setting up I2S Audio..."); +} + +} // namespace i2s_audio +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/i2s_audio/i2s_audio.h b/esphome/components/i2s_audio/i2s_audio.h new file mode 100644 index 0000000000..6b3fa10f3c --- /dev/null +++ b/esphome/components/i2s_audio/i2s_audio.h @@ -0,0 +1,64 @@ +#pragma once + +#ifdef USE_ESP32 + +#include +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace i2s_audio { + +class I2SAudioComponent; + +class I2SAudioIn : public Parented {}; + +class I2SAudioOut : public Parented {}; + +class I2SAudioComponent : public Component { + public: + void setup() override; + + void register_audio_in(I2SAudioIn *in) { + this->audio_in_ = in; + in->set_parent(this); + } + void register_audio_out(I2SAudioOut *out) { + this->audio_out_ = out; + out->set_parent(this); + } + + i2s_pin_config_t get_pin_config() const { + return { + .mck_io_num = I2S_PIN_NO_CHANGE, + .bck_io_num = this->bclk_pin_, + .ws_io_num = this->lrclk_pin_, + .data_out_num = I2S_PIN_NO_CHANGE, + .data_in_num = I2S_PIN_NO_CHANGE, + }; + } + + void set_bclk_pin(uint8_t pin) { this->bclk_pin_ = pin; } + void set_lrclk_pin(uint8_t pin) { this->lrclk_pin_ = pin; } + + void lock() { this->lock_.lock(); } + bool try_lock() { return this->lock_.try_lock(); } + void unlock() { this->lock_.unlock(); } + + i2s_port_t get_port() const { return this->port_; } + + protected: + Mutex lock_; + + I2SAudioIn *audio_in_{nullptr}; + I2SAudioOut *audio_out_{nullptr}; + + uint8_t bclk_pin_; + uint8_t lrclk_pin_; + i2s_port_t port_{}; +}; + +} // namespace i2s_audio +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/i2s_audio/media_player.py b/esphome/components/i2s_audio/media_player/__init__.py similarity index 68% rename from esphome/components/i2s_audio/media_player.py rename to esphome/components/i2s_audio/media_player/__init__.py index 43a48a721e..4ccb9cfc0a 100644 --- a/esphome/components/i2s_audio/media_player.py +++ b/esphome/components/i2s_audio/media_player/__init__.py @@ -5,22 +5,25 @@ import esphome.config_validation as cv from esphome import pins from esphome.const import CONF_ID, CONF_MODE -from esphome.core import CORE + +from .. import ( + i2s_audio_ns, + I2SAudioComponent, + I2SAudioOut, + CONF_I2S_AUDIO_ID, + CONF_I2S_DOUT_PIN, +) CODEOWNERS = ["@jesserockz"] -DEPENDENCIES = ["esp32"] - -i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio") +DEPENDENCIES = ["i2s_audio"] I2SAudioMediaPlayer = i2s_audio_ns.class_( - "I2SAudioMediaPlayer", cg.Component, media_player.MediaPlayer + "I2SAudioMediaPlayer", cg.Component, media_player.MediaPlayer, I2SAudioOut ) i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t") -CONF_I2S_DOUT_PIN = "i2s_dout_pin" -CONF_I2S_BCLK_PIN = "i2s_bclk_pin" -CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin" + CONF_MUTE_PIN = "mute_pin" CONF_AUDIO_ID = "audio_id" CONF_DAC_TYPE = "dac_type" @@ -48,34 +51,26 @@ def validate_esp32_variant(config): CONFIG_SCHEMA = cv.All( cv.typed_schema( { - "internal": cv.Schema( + "internal": media_player.MEDIA_PLAYER_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer), + cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True), } - ) - .extend(media_player.MEDIA_PLAYER_SCHEMA) - .extend(cv.COMPONENT_SCHEMA), - "external": cv.Schema( + ).extend(cv.COMPONENT_SCHEMA), + "external": media_player.MEDIA_PLAYER_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer), + cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), cv.Required( CONF_I2S_DOUT_PIN ): pins.internal_gpio_output_pin_number, - cv.Required( - CONF_I2S_BCLK_PIN - ): pins.internal_gpio_output_pin_number, - cv.Required( - CONF_I2S_LRCLK_PIN - ): pins.internal_gpio_output_pin_number, cv.Optional(CONF_MUTE_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_MODE, default="mono"): cv.one_of( *EXTERNAL_DAC_OPTIONS, lower=True ), } - ) - .extend(media_player.MEDIA_PLAYER_SCHEMA) - .extend(cv.COMPONENT_SCHEMA), + ).extend(cv.COMPONENT_SCHEMA), }, key=CONF_DAC_TYPE, ), @@ -89,19 +84,19 @@ async def to_code(config): await cg.register_component(var, config) await media_player.register_media_player(var, config) + parent = await cg.get_variable(config[CONF_I2S_AUDIO_ID]) + cg.add(parent.register_audio_out(var)) + if config[CONF_DAC_TYPE] == "internal": cg.add(var.set_internal_dac_mode(config[CONF_MODE])) else: cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN])) - cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN])) - cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN])) if CONF_MUTE_PIN in config: pin = await cg.gpio_pin_expression(config[CONF_MUTE_PIN]) cg.add(var.set_mute_pin(pin)) cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1)) - if CORE.is_esp32: - cg.add_library("WiFiClientSecure", None) - cg.add_library("HTTPClient", None) - cg.add_library("esphome/ESP32-audioI2S", "2.0.6") - cg.add_build_flag("-DAUDIO_NO_SD_FS") + cg.add_library("WiFiClientSecure", None) + cg.add_library("HTTPClient", None) + cg.add_library("esphome/ESP32-audioI2S", "2.0.6") + cg.add_build_flag("-DAUDIO_NO_SD_FS") diff --git a/esphome/components/i2s_audio/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp similarity index 72% rename from esphome/components/i2s_audio/i2s_audio_media_player.cpp rename to esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp index 2b00a5ec26..64f83a5ea6 100644 --- a/esphome/components/i2s_audio/i2s_audio_media_player.cpp +++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp @@ -11,11 +11,19 @@ static const char *const TAG = "audio"; void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) { if (call.get_media_url().has_value()) { - if (this->audio_->isRunning()) - this->audio_->stopSong(); - this->high_freq_.start(); - this->audio_->connecttohost(call.get_media_url().value().c_str()); - this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; + this->current_url_ = call.get_media_url(); + + if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && this->audio_ != nullptr) { + if (this->audio_->isRunning()) { + this->audio_->stopSong(); + } + this->audio_->connecttohost(this->current_url_.value().c_str()); + } else { + this->start(); + } + } + if (this->i2s_state_ != I2S_STATE_RUNNING) { + return; } if (call.get_volume().has_value()) { this->volume = call.get_volume().value(); @@ -35,7 +43,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) { this->state = media_player::MEDIA_PLAYER_STATE_PAUSED; break; case media_player::MEDIA_PLAYER_COMMAND_STOP: - this->stop_(); + this->stop(); break; case media_player::MEDIA_PLAYER_COMMAND_MUTE: this->mute_(); @@ -94,22 +102,51 @@ void I2SAudioMediaPlayer::set_volume_(float volume, bool publish) { this->volume = volume; } -void I2SAudioMediaPlayer::stop_() { - if (this->audio_->isRunning()) - this->audio_->stopSong(); - this->high_freq_.stop(); +void I2SAudioMediaPlayer::setup() { + ESP_LOGCONFIG(TAG, "Setting up Audio..."); this->state = media_player::MEDIA_PLAYER_STATE_IDLE; } -void I2SAudioMediaPlayer::setup() { - ESP_LOGCONFIG(TAG, "Setting up Audio..."); +void I2SAudioMediaPlayer::loop() { + switch (this->i2s_state_) { + case I2S_STATE_STARTING: + this->start_(); + break; + case I2S_STATE_RUNNING: + this->play_(); + break; + case I2S_STATE_STOPPING: + this->stop_(); + break; + case I2S_STATE_STOPPED: + break; + } +} + +void I2SAudioMediaPlayer::play_() { + this->audio_->loop(); + if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) { + this->stop(); + } +} + +void I2SAudioMediaPlayer::start() { this->i2s_state_ = I2S_STATE_STARTING; } +void I2SAudioMediaPlayer::start_() { + if (this->parent_->try_lock()) { + return; // Waiting for another i2s to return lock + } + #if SOC_I2S_SUPPORTS_DAC if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) { - this->audio_ = make_unique