mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Merge branch 'dev' into bump-2023.4.0b1
This commit is contained in:
		| @@ -4,12 +4,17 @@ | ||||
|   "postCreateCommand": [ | ||||
|     "script/devcontainer-post-create" | ||||
|   ], | ||||
|   "containerEnv": { | ||||
|     "DEVCONTAINER": "1" | ||||
|   }, | ||||
|   "runArgs": [ | ||||
|     "--privileged", | ||||
|     "-e", | ||||
|     "ESPHOME_DASHBOARD_USE_PING=1" | ||||
|   ], | ||||
|   "appPort": 6052, | ||||
|   "customizations": { | ||||
|     "vscode": { | ||||
|       "extensions": [ | ||||
|         // python | ||||
|         "ms-python.python", | ||||
| @@ -51,6 +56,8 @@ | ||||
|         "files.associations": { | ||||
|           "**/.vscode/*.json": "jsonc" | ||||
|         }, | ||||
|     "C_Cpp.clang_format_path": "/usr/bin/clang-format-11", | ||||
|         "C_Cpp.clang_format_path": "/usr/bin/clang-format-13" | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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' | ||||
|  | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
							
								
								
									
										60
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 <esphome@nabucasa.com> | ||||
|           author: esphomebot <esphome@nabucasa.com> | ||||
|           branch: sync/device-classes/ | ||||
|           branch-suffix: timestamp | ||||
|           delete-branch: true | ||||
|           title: "Synchronise Device Classes from Home Assistant" | ||||
|           body: ${{ steps.pr-template-body.outputs.body }} | ||||
| @@ -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: | ||||
|   | ||||
							
								
								
									
										15
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							| @@ -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", | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 \ | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -76,8 +76,6 @@ async def to_code(config): | ||||
|         pos = 0 | ||||
|         for frameIndex in range(frames): | ||||
|             image.seek(frameIndex) | ||||
|             if CONF_RESIZE in config: | ||||
|                 image.thumbnail(config[CONF_RESIZE]) | ||||
|             frame = image.convert("RGB") | ||||
|             if CONF_RESIZE in config: | ||||
|                 frame = frame.resize([width, height]) | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include "api_connection.h" | ||||
| #include <cerrno> | ||||
| #include <cinttypes> | ||||
| #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,6 +184,7 @@ 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(); | ||||
|   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(); | ||||
| @@ -212,6 +217,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) { | ||||
|   ListEntitiesCoverResponse msg; | ||||
|   msg.key = cover->get_object_id_hash(); | ||||
|   msg.object_id = cover->get_object_id(); | ||||
|   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(); | ||||
| @@ -275,6 +281,7 @@ bool APIConnection::send_fan_info(fan::Fan *fan) { | ||||
|   ListEntitiesFanResponse msg; | ||||
|   msg.key = fan->get_object_id_hash(); | ||||
|   msg.object_id = fan->get_object_id(); | ||||
|   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(); | ||||
| @@ -337,6 +344,7 @@ bool APIConnection::send_light_info(light::LightState *light) { | ||||
|   ListEntitiesLightResponse msg; | ||||
|   msg.key = light->get_object_id_hash(); | ||||
|   msg.object_id = light->get_object_id(); | ||||
|   if (light->has_own_name()) | ||||
|     msg.name = light->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("light", light); | ||||
|  | ||||
| @@ -418,6 +426,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { | ||||
|   ListEntitiesSensorResponse msg; | ||||
|   msg.key = sensor->get_object_id_hash(); | ||||
|   msg.object_id = sensor->get_object_id(); | ||||
|   if (sensor->has_own_name()) | ||||
|     msg.name = sensor->get_name(); | ||||
|   msg.unique_id = sensor->unique_id(); | ||||
|   if (msg.unique_id.empty()) | ||||
| @@ -448,6 +457,7 @@ 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(); | ||||
|   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(); | ||||
| @@ -533,6 +543,7 @@ bool APIConnection::send_climate_info(climate::Climate *climate) { | ||||
|   ListEntitiesClimateResponse msg; | ||||
|   msg.key = climate->get_object_id_hash(); | ||||
|   msg.object_id = climate->get_object_id(); | ||||
|   if (climate->has_own_name()) | ||||
|     msg.name = climate->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("climate", climate); | ||||
|  | ||||
| @@ -611,6 +622,7 @@ bool APIConnection::send_number_info(number::Number *number) { | ||||
|   ListEntitiesNumberResponse msg; | ||||
|   msg.key = number->get_object_id_hash(); | ||||
|   msg.object_id = number->get_object_id(); | ||||
|   if (number->has_own_name()) | ||||
|     msg.name = number->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("number", number); | ||||
|   msg.icon = number->get_icon(); | ||||
| @@ -652,6 +664,7 @@ bool APIConnection::send_select_info(select::Select *select) { | ||||
|   ListEntitiesSelectResponse msg; | ||||
|   msg.key = select->get_object_id_hash(); | ||||
|   msg.object_id = select->get_object_id(); | ||||
|   if (select->has_own_name()) | ||||
|     msg.name = select->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("select", select); | ||||
|   msg.icon = select->get_icon(); | ||||
| @@ -679,6 +692,7 @@ bool APIConnection::send_button_info(button::Button *button) { | ||||
|   ListEntitiesButtonResponse msg; | ||||
|   msg.key = button->get_object_id_hash(); | ||||
|   msg.object_id = button->get_object_id(); | ||||
|   if (button->has_own_name()) | ||||
|     msg.name = button->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("button", button); | ||||
|   msg.icon = button->get_icon(); | ||||
| @@ -710,6 +724,7 @@ 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(); | ||||
|   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(); | ||||
| @@ -755,6 +770,7 @@ 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(); | ||||
|   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(); | ||||
| @@ -799,6 +815,7 @@ 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(); | ||||
|   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(); | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| #include "api_server.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/defines.h" | ||||
|  | ||||
| #include <vector> | ||||
|  | ||||
| @@ -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_; | ||||
|   | ||||
| @@ -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]; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -400,6 +400,34 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(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>(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<enums::VoiceAssistantEvent>(); | ||||
|       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<VoiceAssistantEventData>()); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_enum<enums::VoiceAssistantEvent>(1, this->event_type); | ||||
|   for (auto &it : this->data) { | ||||
|     buffer.encode_message<VoiceAssistantEventData>(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<enums::VoiceAssistantEvent>(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 | ||||
|   | ||||
| @@ -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<VoiceAssistantEventData> 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 | ||||
|   | ||||
| @@ -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_<BluetoothDeviceUnpairingResponse>(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_<BluetoothDeviceClearCacheResponse>(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_<VoiceAssistantRequest>(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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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,18 @@ void APIServer::on_shutdown() { | ||||
|   delay(10); | ||||
| } | ||||
|  | ||||
| #ifdef USE_VOICE_ASSISTANT | ||||
| void APIServer::start_voice_assistant() { | ||||
|   for (auto &c : this->clients_) { | ||||
|     c->request_voice_assistant(true); | ||||
|   } | ||||
| } | ||||
| void APIServer::stop_voice_assistant() { | ||||
|   for (auto &c : this->clients_) { | ||||
|     c->request_voice_assistant(false); | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| }  // namespace api | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -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 | ||||
|   void start_voice_assistant(); | ||||
|   void stop_voice_assistant(); | ||||
| #endif | ||||
|  | ||||
|   bool is_connected() const; | ||||
|  | ||||
|   struct HomeAssistantStateSubscription { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #include "proto.h" | ||||
| #include <cinttypes> | ||||
| #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; | ||||
|     } | ||||
|   | ||||
| @@ -12,7 +12,6 @@ from esphome.const import ( | ||||
|     CONF_CAPACITANCE, | ||||
| ) | ||||
|  | ||||
| AUTO_LOAD = ["sensor", "binary_sensor"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| CONF_AS3935_ID = "as3935_id" | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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_; | ||||
|   | ||||
| @@ -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<BedjetCodec>(); } | ||||
|   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() { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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<void(bool)> state_callback_{}; | ||||
|   optional<std::string> device_class_{};  ///< Stores the override of the device class | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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,7 +32,8 @@ BME680BSECComponent = bme680_bsec_ns.class_( | ||||
|     "BME680BSECComponent", cg.Component, i2c.I2CDevice | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(BME680BSECComponent), | ||||
|             cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature, | ||||
| @@ -45,9 +46,17 @@ CONFIG_SCHEMA = cv.Schema( | ||||
|             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): | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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<uint16_t>(lroundf(humidity * 512.0f + 0.5f)); | ||||
|   uint16_t temp_conv = static_cast<uint16_t>(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"); | ||||
|   | ||||
| @@ -324,6 +324,10 @@ async def setup_climate_core_(var, config): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_CONTROL, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|  | ||||
|  | ||||
| async def register_climate(var, config): | ||||
|     if not CORE.has_id(config[CONF_ID]): | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -14,12 +14,15 @@ from .. import copy_ns | ||||
| CopySelect = copy_ns.class_("CopySelect", select.Select, cg.Component) | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = select.SELECT_SCHEMA.extend( | ||||
| CONFIG_SCHEMA = ( | ||||
|     select.select_schema(CopySelect) | ||||
|     .extend( | ||||
|         { | ||||
|         cv.GenerateID(): cv.declare_id(CopySelect), | ||||
|             cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select), | ||||
|         } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|     ) | ||||
|     .extend(cv.COMPONENT_SCHEMA) | ||||
| ) | ||||
|  | ||||
| FINAL_VALIDATE_SCHEMA = cv.All( | ||||
|     inherit_property_from(CONF_ICON, CONF_SOURCE_ID), | ||||
|   | ||||
| @@ -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<CoverRestoreState> 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(); | ||||
|   | ||||
| @@ -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<CoverRestoreState> restore_state_(); | ||||
|  | ||||
|   CallbackManager<void()> state_callback_{}; | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -61,7 +61,7 @@ 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[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)); | ||||
|   | ||||
| @@ -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]; | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -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); } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "gpio.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include <cinttypes> | ||||
|  | ||||
| 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<uint32_t>(pin_)); | ||||
|   snprintf(buffer, sizeof(buffer), "GPIO%" PRIu32, static_cast<uint32_t>(pin_)); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include "esphome/core/log.h" | ||||
| #include <nvs_flash.h> | ||||
| #include <cstring> | ||||
| #include <cinttypes> | ||||
| #include <vector> | ||||
| #include <string> | ||||
|  | ||||
| @@ -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); | ||||
|   } | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| #include <esp_bt.h> | ||||
| #include <esp_bt_main.h> | ||||
| #include <esp_bt_device.h> | ||||
| #include <esp_gap_ble_api.h> | ||||
| #include <freertos/FreeRTOS.h> | ||||
| #include <freertos/FreeRTOSConfig.h> | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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_, | ||||
|   | ||||
| @@ -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_; } | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|   | ||||
| @@ -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; } | ||||
|   | ||||
| @@ -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 */ | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -74,6 +74,10 @@ void EthernetComponent::setup() { | ||||
|       phy = esp_eth_phy_new_jl1101(&phy_config); | ||||
|       break; | ||||
|     } | ||||
|     case ETHERNET_TYPE_KSZ8081: { | ||||
|       phy = esp_eth_phy_new_ksz8081(&phy_config); | ||||
|       break; | ||||
|     } | ||||
|     default: { | ||||
|       this->mark_failed(); | ||||
|       return; | ||||
| @@ -140,7 +144,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 +162,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 +182,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 +268,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<uint32_t>(this->manual_ip_->dns1); | ||||
| #else | ||||
|       d.addr = static_cast<uint32_t>(this->manual_ip_->dns1); | ||||
| #endif | ||||
|       dns_setserver(0, &d); | ||||
|     } | ||||
|     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<uint32_t>(this->manual_ip_->dns2); | ||||
| #else | ||||
|       d.addr = static_cast<uint32_t>(this->manual_ip_->dns2); | ||||
| #endif | ||||
|       dns_setserver(1, &d); | ||||
|     } | ||||
|   } else { | ||||
| @@ -289,8 +310,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; | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
| @@ -69,7 +71,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<ManualIP> manual_ip_{}; | ||||
|   | ||||
| @@ -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 }; | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -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); } | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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_; } | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
|   | ||||
| @@ -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; } | ||||
|   | ||||
| @@ -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)); | ||||
|   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_)) { | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
| @@ -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_); | ||||
|   } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include <cstring> | ||||
| #include <cinttypes> | ||||
|  | ||||
| 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"); | ||||
|   | ||||
| @@ -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])) | ||||
|   | ||||
							
								
								
									
										30
									
								
								esphome/components/i2s_audio/i2s_audio.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/i2s_audio/i2s_audio.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										64
									
								
								esphome/components/i2s_audio/i2s_audio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								esphome/components/i2s_audio/i2s_audio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| #pragma once | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
|  | ||||
| #include <driver/i2s.h> | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2s_audio { | ||||
|  | ||||
| class I2SAudioComponent; | ||||
|  | ||||
| class I2SAudioIn : public Parented<I2SAudioComponent> {}; | ||||
|  | ||||
| class I2SAudioOut : public Parented<I2SAudioComponent> {}; | ||||
|  | ||||
| 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 | ||||
| @@ -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,18 +84,18 @@ 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") | ||||
| @@ -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->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->high_freq_.start(); | ||||
|     this->audio_->connecttohost(call.get_media_url().value().c_str()); | ||||
|     this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; | ||||
|       } | ||||
|       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<Audio>(true, this->internal_dac_mode_); | ||||
|     this->audio_ = make_unique<Audio>(true, this->internal_dac_mode_, this->parent_->get_port()); | ||||
|   } else { | ||||
| #endif | ||||
|     this->audio_ = make_unique<Audio>(false); | ||||
|     this->audio_->setPinout(this->bclk_pin_, this->lrclk_pin_, this->dout_pin_); | ||||
|     this->audio_ = make_unique<Audio>(false, I2S_DAC_CHANNEL_BOTH_EN, this->parent_->get_port()); | ||||
| 
 | ||||
|     i2s_pin_config_t pin_config = this->parent_->get_pin_config(); | ||||
|     pin_config.data_out_num = this->dout_pin_; | ||||
|     i2s_set_pin(this->parent_->get_port(), &pin_config); | ||||
| 
 | ||||
|     this->audio_->forceMono(this->external_dac_channels_ == 1); | ||||
|     if (this->mute_pin_ != nullptr) { | ||||
|       this->mute_pin_->setup(); | ||||
| @@ -118,16 +155,30 @@ void I2SAudioMediaPlayer::setup() { | ||||
| #if SOC_I2S_SUPPORTS_DAC | ||||
|   } | ||||
| #endif | ||||
|   this->state = media_player::MEDIA_PLAYER_STATE_IDLE; | ||||
| } | ||||
| 
 | ||||
| void I2SAudioMediaPlayer::loop() { | ||||
|   this->audio_->loop(); | ||||
|   if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) { | ||||
|     this->stop_(); | ||||
|   this->i2s_state_ = I2S_STATE_RUNNING; | ||||
|   this->high_freq_.start(); | ||||
|   if (this->current_url_.has_value()) { | ||||
|     this->audio_->connecttohost(this->current_url_.value().c_str()); | ||||
|     this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; | ||||
|     this->publish_state(); | ||||
|   } | ||||
| } | ||||
| void I2SAudioMediaPlayer::stop() { this->i2s_state_ = I2S_STATE_STOPPING; } | ||||
| void I2SAudioMediaPlayer::stop_() { | ||||
|   if (this->audio_->isRunning()) { | ||||
|     this->audio_->stopSong(); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   this->audio_ = nullptr; | ||||
|   this->current_url_ = {}; | ||||
|   this->parent_->unlock(); | ||||
|   this->i2s_state_ = I2S_STATE_STOPPED; | ||||
| 
 | ||||
|   this->high_freq_.stop(); | ||||
|   this->state = media_player::MEDIA_PLAYER_STATE_IDLE; | ||||
|   this->publish_state(); | ||||
| } | ||||
| 
 | ||||
| media_player::MediaPlayerTraits I2SAudioMediaPlayer::get_traits() { | ||||
|   auto traits = media_player::MediaPlayerTraits(); | ||||
| @@ -2,6 +2,10 @@ | ||||
| 
 | ||||
| #ifdef USE_ESP32_FRAMEWORK_ARDUINO | ||||
| 
 | ||||
| #include "../i2s_audio.h" | ||||
| 
 | ||||
| #include <driver/i2s.h> | ||||
| 
 | ||||
| #include "esphome/components/media_player/media_player.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/gpio.h" | ||||
| @@ -12,7 +16,14 @@ | ||||
| namespace esphome { | ||||
| namespace i2s_audio { | ||||
| 
 | ||||
| class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer { | ||||
| enum I2SState : uint8_t { | ||||
|   I2S_STATE_STOPPED = 0, | ||||
|   I2S_STATE_STARTING, | ||||
|   I2S_STATE_RUNNING, | ||||
|   I2S_STATE_STOPPING, | ||||
| }; | ||||
| 
 | ||||
| class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer, public I2SAudioOut { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   float get_setup_priority() const override { return esphome::setup_priority::LATE; } | ||||
| @@ -22,8 +33,6 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer { | ||||
|   void dump_config() override; | ||||
| 
 | ||||
|   void set_dout_pin(uint8_t pin) { this->dout_pin_ = pin; } | ||||
|   void set_bclk_pin(uint8_t pin) { this->bclk_pin_ = pin; } | ||||
|   void set_lrclk_pin(uint8_t pin) { this->lrclk_pin_ = pin; } | ||||
|   void set_mute_pin(GPIOPin *mute_pin) { this->mute_pin_ = mute_pin; } | ||||
| #if SOC_I2S_SUPPORTS_DAC | ||||
|   void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; } | ||||
| @@ -34,20 +43,24 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer { | ||||
| 
 | ||||
|   bool is_muted() const override { return this->muted_; } | ||||
| 
 | ||||
|   void start(); | ||||
|   void stop(); | ||||
| 
 | ||||
|  protected: | ||||
|   void control(const media_player::MediaPlayerCall &call) override; | ||||
| 
 | ||||
|   void mute_(); | ||||
|   void unmute_(); | ||||
|   void set_volume_(float volume, bool publish = true); | ||||
|   void stop_(); | ||||
| 
 | ||||
|   void start_(); | ||||
|   void stop_(); | ||||
|   void play_(); | ||||
| 
 | ||||
|   I2SState i2s_state_{I2S_STATE_STOPPED}; | ||||
|   std::unique_ptr<Audio> audio_; | ||||
| 
 | ||||
|   uint8_t dout_pin_{0}; | ||||
|   uint8_t din_pin_{0}; | ||||
|   uint8_t bclk_pin_; | ||||
|   uint8_t lrclk_pin_; | ||||
| 
 | ||||
|   GPIOPin *mute_pin_{nullptr}; | ||||
|   bool muted_{false}; | ||||
| @@ -59,6 +72,8 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer { | ||||
|   uint8_t external_dac_channels_; | ||||
| 
 | ||||
|   HighFrequencyLoopRequester high_freq_; | ||||
| 
 | ||||
|   optional<std::string> current_url_{}; | ||||
| }; | ||||
| 
 | ||||
| }  // namespace i2s_audio
 | ||||
							
								
								
									
										41
									
								
								esphome/components/i2s_audio/microphone/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								esphome/components/i2s_audio/microphone/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| import esphome.config_validation as cv | ||||
| import esphome.codegen as cg | ||||
|  | ||||
| from esphome import pins | ||||
| from esphome.const import CONF_ID | ||||
| from esphome.components import microphone | ||||
|  | ||||
| from .. import ( | ||||
|     i2s_audio_ns, | ||||
|     I2SAudioComponent, | ||||
|     I2SAudioIn, | ||||
|     CONF_I2S_AUDIO_ID, | ||||
|     CONF_I2S_DIN_PIN, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@jesserockz"] | ||||
| DEPENDENCIES = ["i2s_audio"] | ||||
|  | ||||
| I2SAudioMicrophone = i2s_audio_ns.class_( | ||||
|     "I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = microphone.MICROPHONE_SCHEMA.extend( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(I2SAudioMicrophone), | ||||
|         cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), | ||||
|         cv.Required(CONF_I2S_DIN_PIN): pins.internal_gpio_input_pin_number, | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|  | ||||
|     parent = await cg.get_variable(config[CONF_I2S_AUDIO_ID]) | ||||
|     cg.add(parent.register_audio_in(var)) | ||||
|  | ||||
|     cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN])) | ||||
|  | ||||
|     await microphone.register_microphone(var, config) | ||||
							
								
								
									
										101
									
								
								esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| #include "i2s_audio_microphone.h" | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
|  | ||||
| #include <driver/i2s.h> | ||||
|  | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2s_audio { | ||||
|  | ||||
| static const size_t BUFFER_SIZE = 512; | ||||
|  | ||||
| static const char *const TAG = "i2s_audio.microphone"; | ||||
|  | ||||
| void I2SAudioMicrophone::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone..."); | ||||
|   this->buffer_.resize(BUFFER_SIZE); | ||||
| } | ||||
|  | ||||
| void I2SAudioMicrophone::start() { this->state_ = microphone::STATE_STARTING; } | ||||
| void I2SAudioMicrophone::start_() { | ||||
|   if (!this->parent_->try_lock()) { | ||||
|     return;  // Waiting for another i2s to return lock | ||||
|   } | ||||
|   i2s_driver_config_t config = { | ||||
|       .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM), | ||||
|       .sample_rate = 16000, | ||||
|       .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, | ||||
|       .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, | ||||
|       .communication_format = I2S_COMM_FORMAT_STAND_I2S, | ||||
|       .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, | ||||
|       .dma_buf_count = 4, | ||||
|       .dma_buf_len = 256, | ||||
|       .use_apll = false, | ||||
|       .tx_desc_auto_clear = false, | ||||
|       .fixed_mclk = 0, | ||||
|       .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, | ||||
|       .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT, | ||||
|   }; | ||||
|  | ||||
|   i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr); | ||||
|  | ||||
|   i2s_pin_config_t pin_config = this->parent_->get_pin_config(); | ||||
|   pin_config.data_in_num = this->din_pin_; | ||||
|  | ||||
|   i2s_set_pin(this->parent_->get_port(), &pin_config); | ||||
|   this->state_ = microphone::STATE_RUNNING; | ||||
|   this->high_freq_.start(); | ||||
| } | ||||
|  | ||||
| void I2SAudioMicrophone::stop() { | ||||
|   if (this->state_ == microphone::STATE_STOPPED) | ||||
|     return; | ||||
|   this->state_ = microphone::STATE_STOPPING; | ||||
| } | ||||
|  | ||||
| void I2SAudioMicrophone::stop_() { | ||||
|   i2s_stop(this->parent_->get_port()); | ||||
|   i2s_driver_uninstall(this->parent_->get_port()); | ||||
|   this->parent_->unlock(); | ||||
|   this->state_ = microphone::STATE_STOPPED; | ||||
|   this->high_freq_.stop(); | ||||
| } | ||||
|  | ||||
| void I2SAudioMicrophone::read_() { | ||||
|   size_t bytes_read = 0; | ||||
|   esp_err_t err = | ||||
|       i2s_read(this->parent_->get_port(), this->buffer_.data(), BUFFER_SIZE, &bytes_read, (100 / portTICK_PERIOD_MS)); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err)); | ||||
|     this->status_set_warning(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   this->status_clear_warning(); | ||||
|  | ||||
|   this->data_callbacks_.call(this->buffer_); | ||||
| } | ||||
|  | ||||
| void I2SAudioMicrophone::loop() { | ||||
|   switch (this->state_) { | ||||
|     case microphone::STATE_STOPPED: | ||||
|       break; | ||||
|     case microphone::STATE_STARTING: | ||||
|       this->start_(); | ||||
|       break; | ||||
|     case microphone::STATE_RUNNING: | ||||
|       this->read_(); | ||||
|       break; | ||||
|     case microphone::STATE_STOPPING: | ||||
|       this->stop_(); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace i2s_audio | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif  // USE_ESP32 | ||||
| @@ -0,0 +1,37 @@ | ||||
| #pragma once | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
|  | ||||
| #include "../i2s_audio.h" | ||||
|  | ||||
| #include "esphome/components/microphone/microphone.h" | ||||
| #include "esphome/core/component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2s_audio { | ||||
|  | ||||
| class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void start() override; | ||||
|   void stop() override; | ||||
|  | ||||
|   void loop() override; | ||||
|  | ||||
|   void set_din_pin(uint8_t pin) { this->din_pin_ = pin; } | ||||
|  | ||||
|  protected: | ||||
|   void start_(); | ||||
|   void stop_(); | ||||
|   void read_(); | ||||
|  | ||||
|   uint8_t din_pin_{0}; | ||||
|   std::vector<uint8_t> buffer_; | ||||
|  | ||||
|   HighFrequencyLoopRequester high_freq_; | ||||
| }; | ||||
|  | ||||
| }  // namespace i2s_audio | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif  // USE_ESP32 | ||||
| @@ -1,8 +1,8 @@ | ||||
| #include "ili9xxx_display.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ili9xxx { | ||||
| @@ -85,7 +85,7 @@ void ILI9XXXDisplay::fill(Color color) { | ||||
|     case BITS_16: | ||||
|       new_color = display::ColorUtil::color_to_565(color); | ||||
|       for (uint32_t i = 0; i < this->get_buffer_length_() * 2; i = i + 2) { | ||||
|         this->buffer_[i] = (uint8_t)(new_color >> 8); | ||||
|         this->buffer_[i] = (uint8_t) (new_color >> 8); | ||||
|         this->buffer_[i + 1] = (uint8_t) new_color; | ||||
|       } | ||||
|       return; | ||||
| @@ -111,8 +111,8 @@ void HOT ILI9XXXDisplay::draw_absolute_pixel_internal(int x, int y, Color color) | ||||
|     case BITS_16: | ||||
|       pos = pos * 2; | ||||
|       new_color = display::ColorUtil::color_to_565(color, display::ColorOrder::COLOR_ORDER_RGB); | ||||
|       if (this->buffer_[pos] != (uint8_t)(new_color >> 8)) { | ||||
|         this->buffer_[pos] = (uint8_t)(new_color >> 8); | ||||
|       if (this->buffer_[pos] != (uint8_t) (new_color >> 8)) { | ||||
|         this->buffer_[pos] = (uint8_t) (new_color >> 8); | ||||
|         updated = true; | ||||
|       } | ||||
|       pos = pos + 1; | ||||
| @@ -192,9 +192,9 @@ void ILI9XXXDisplay::display_() { | ||||
|  | ||||
|           uint8_t pass_buff[3]; | ||||
|  | ||||
|           pass_buff[2] = (uint8_t)((red / 32.0) * 64) << 2; | ||||
|           pass_buff[2] = (uint8_t) ((red / 32.0) * 64) << 2; | ||||
|           pass_buff[1] = (uint8_t) green << 2; | ||||
|           pass_buff[0] = (uint8_t)((blue / 32.0) * 64) << 2; | ||||
|           pass_buff[0] = (uint8_t) ((blue / 32.0) * 64) << 2; | ||||
|  | ||||
|           this->write_array(pass_buff, sizeof(pass_buff)); | ||||
|         } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ std::string ImprovBase::get_formatted_next_url_() { | ||||
|   // Ip address | ||||
|   pos = this->next_url_.find("{{ip_address}}"); | ||||
|   if (pos != std::string::npos) { | ||||
|     std::string ip = network::IPAddress(network::get_ip_address()).str(); | ||||
|     std::string ip = network::get_ip_address().str(); | ||||
|     copy.replace(pos, 14, ip); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -73,7 +73,7 @@ void parse_json(const std::string &data, const json_parse_t &f) { | ||||
|   const size_t free_heap = rp2040.getFreeHeap(); | ||||
| #endif | ||||
|   bool pass = false; | ||||
|   size_t request_size = std::min(free_heap, (size_t)(data.size() * 1.5)); | ||||
|   size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); | ||||
|   do { | ||||
|     DynamicJsonDocument json_document(request_size); | ||||
|     if (json_document.capacity() == 0) { | ||||
|   | ||||
| @@ -17,7 +17,7 @@ void GPIOLCDDisplay::setup() { | ||||
|   this->enable_pin_->setup();  // OUTPUT | ||||
|   this->enable_pin_->digital_write(false); | ||||
|  | ||||
|   for (uint8_t i = 0; i < (uint8_t)(this->is_four_bit_mode() ? 4u : 8u); i++) { | ||||
|   for (uint8_t i = 0; i < (uint8_t) (this->is_four_bit_mode() ? 4u : 8u); i++) { | ||||
|     this->data_pins_[i]->setup();  // OUTPUT | ||||
|     this->data_pins_[i]->digital_write(false); | ||||
|   } | ||||
|   | ||||
| @@ -118,7 +118,7 @@ class LD2410Component : public Component, public uart::UARTDevice { | ||||
| #endif | ||||
|  | ||||
|   std::vector<uint8_t> rx_buffer_; | ||||
|   int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t)(secondbyte << 8) + firstbyte; } | ||||
|   int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; } | ||||
|   void send_command_(uint8_t command_str, uint8_t *command_value, int command_value_len); | ||||
|  | ||||
|   void set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range, | ||||
|   | ||||
| @@ -60,7 +60,7 @@ LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ex | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(LightState), | ||||
|         cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent), | ||||
|         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.Optional(CONF_ON_TURN_ON): auto.validate_automation( | ||||
|   | ||||
| @@ -52,24 +52,25 @@ enum class ColorMode : uint8_t { | ||||
|   /// Only on/off control. | ||||
|   ON_OFF = (uint8_t) ColorCapability::ON_OFF, | ||||
|   /// Dimmable light. | ||||
|   BRIGHTNESS = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS), | ||||
|   BRIGHTNESS = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS), | ||||
|   /// White output only (use only if the light also has another color mode such as RGB). | ||||
|   WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::WHITE), | ||||
|   WHITE = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::WHITE), | ||||
|   /// Controllable color temperature output. | ||||
|   COLOR_TEMPERATURE = | ||||
|       (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLOR_TEMPERATURE), | ||||
|       (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLOR_TEMPERATURE), | ||||
|   /// Cold and warm white output with individually controllable brightness. | ||||
|   COLD_WARM_WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLD_WARM_WHITE), | ||||
|   COLD_WARM_WHITE = | ||||
|       (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLD_WARM_WHITE), | ||||
|   /// RGB color output. | ||||
|   RGB = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB), | ||||
|   RGB = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB), | ||||
|   /// RGB color output and a separate white output. | ||||
|   RGB_WHITE = | ||||
|       (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | ColorCapability::WHITE), | ||||
|       (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | ColorCapability::WHITE), | ||||
|   /// RGB color output and a separate white output with controllable color temperature. | ||||
|   RGB_COLOR_TEMPERATURE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | | ||||
|   RGB_COLOR_TEMPERATURE = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | | ||||
|                                      ColorCapability::WHITE | ColorCapability::COLOR_TEMPERATURE), | ||||
|   /// RGB color output, and separate cold and warm white outputs. | ||||
|   RGB_COLD_WARM_WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | | ||||
|   RGB_COLD_WARM_WHITE = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | | ||||
|                                    ColorCapability::COLD_WARM_WHITE), | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,6 @@ namespace light { | ||||
|  | ||||
| static const char *const TAG = "light"; | ||||
|  | ||||
| LightState::LightState(const std::string &name, LightOutput *output) : EntityBase(name), output_(output) {} | ||||
| LightState::LightState(LightOutput *output) : output_(output) {} | ||||
|  | ||||
| LightTraits LightState::get_traits() { return this->output_->get_traits(); } | ||||
|   | ||||
| @@ -33,9 +33,6 @@ enum LightRestoreMode { | ||||
|  */ | ||||
| class LightState : public EntityBase, public Component { | ||||
|  public: | ||||
|   /// Construct this LightState using the provided traits and name. | ||||
|   LightState(const std::string &name, LightOutput *output); | ||||
|  | ||||
|   LightState(LightOutput *output); | ||||
|  | ||||
|   LightTraits get_traits(); | ||||
|   | ||||
| @@ -113,8 +113,8 @@ void LilygoT547Touchscreen::loop() { | ||||
|       if (tp.state == 0x06) | ||||
|         tp.state = 0x07; | ||||
|  | ||||
|       uint16_t y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F)); | ||||
|       uint16_t x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F)); | ||||
|       uint16_t y = (uint16_t) ((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F)); | ||||
|       uint16_t x = (uint16_t) ((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F)); | ||||
|  | ||||
|       switch (this->rotation_) { | ||||
|         case ROTATE_0_DEGREES: | ||||
| @@ -142,8 +142,8 @@ void LilygoT547Touchscreen::loop() { | ||||
|     tp.id = (buffer[0] >> 4) & 0x0F; | ||||
|     tp.state = 0x06; | ||||
|  | ||||
|     uint16_t y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F)); | ||||
|     uint16_t x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F)); | ||||
|     uint16_t y = (uint16_t) ((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F)); | ||||
|     uint16_t x = (uint16_t) ((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F)); | ||||
|  | ||||
|     switch (this->rotation_) { | ||||
|       case ROTATE_0_DEGREES: | ||||
|   | ||||
| @@ -24,8 +24,7 @@ const char *lock_state_to_string(LockState state) { | ||||
|   } | ||||
| } | ||||
|  | ||||
| Lock::Lock(const std::string &name) : EntityBase(name), state(LOCK_STATE_NONE) {} | ||||
| Lock::Lock() : Lock("") {} | ||||
| Lock::Lock() : state(LOCK_STATE_NONE) {} | ||||
| LockCall Lock::make_call() { return LockCall(this); } | ||||
|  | ||||
| void Lock::lock() { | ||||
|   | ||||
| @@ -103,7 +103,6 @@ class LockCall { | ||||
| class Lock : public EntityBase { | ||||
|  public: | ||||
|   explicit Lock(); | ||||
|   explicit Lock(const std::string &name); | ||||
|  | ||||
|   /** Make a lock device control call, this is used to control the lock device, see the LockCall description | ||||
|    * for more info. | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #include "logger.h" | ||||
| #include <cinttypes> | ||||
|  | ||||
| #ifdef USE_ESP_IDF | ||||
| #include <driver/uart.h> | ||||
| @@ -292,7 +293,7 @@ const char *const UART_SELECTIONS[] = {"UART0", "UART1", "USB_CDC"}; | ||||
| void Logger::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "Logger:"); | ||||
|   ESP_LOGCONFIG(TAG, "  Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); | ||||
|   ESP_LOGCONFIG(TAG, "  Log Baud Rate: %u", this->baud_rate_); | ||||
|   ESP_LOGCONFIG(TAG, "  Log Baud Rate: %" PRIu32, this->baud_rate_); | ||||
|   ESP_LOGCONFIG(TAG, "  Hardware UART: %s", UART_SELECTIONS[this->uart_]); | ||||
|   for (auto &it : this->log_levels_) { | ||||
|     ESP_LOGCONFIG(TAG, "  Level for '%s': %s", it.tag.c_str(), LOG_LEVELS[it.level]); | ||||
|   | ||||
| @@ -7,6 +7,7 @@ CODEOWNERS = ["@rspaargaren"] | ||||
| DEPENDENCIES = ["spi"] | ||||
|  | ||||
| CONF_ROTATE_CHIP = "rotate_chip" | ||||
| CONF_FLIP_X = "flip_x" | ||||
| CONF_SCROLL_SPEED = "scroll_speed" | ||||
| CONF_SCROLL_DWELL = "scroll_dwell" | ||||
| CONF_SCROLL_DELAY = "scroll_delay" | ||||
| @@ -67,6 +68,7 @@ CONFIG_SCHEMA = ( | ||||
|                 CONF_SCROLL_DWELL, default="1000ms" | ||||
|             ): cv.positive_time_period_milliseconds, | ||||
|             cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean, | ||||
|             cv.Optional(CONF_FLIP_X, default=False): cv.boolean, | ||||
|         } | ||||
|     ) | ||||
|     .extend(cv.polling_component_schema("500ms")) | ||||
| @@ -91,6 +93,7 @@ async def to_code(config): | ||||
|     cg.add(var.set_scroll(config[CONF_SCROLL_ENABLE])) | ||||
|     cg.add(var.set_scroll_mode(config[CONF_SCROLL_MODE])) | ||||
|     cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE])) | ||||
|     cg.add(var.set_flip_x([CONF_FLIP_X])) | ||||
|  | ||||
|     if CONF_LAMBDA in config: | ||||
|         lambda_ = await cg.process_lambda( | ||||
|   | ||||
| @@ -261,14 +261,22 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) { | ||||
|     if (this->orientation_ == 0) { | ||||
|       for (uint8_t i = 0; i < 8; i++) { | ||||
|         // run this loop 8 times for all the pixels[8] received | ||||
|         if (this->flip_x_) { | ||||
|           b |= ((pixels[i] >> col) & 1) << i;  // change the column bits into row bits | ||||
|         } else { | ||||
|           b |= ((pixels[i] >> col) & 1) << (7 - i);  // change the column bits into row bits | ||||
|         } | ||||
|       } | ||||
|     } else if (this->orientation_ == 1) { | ||||
|       b = pixels[col]; | ||||
|     } else if (this->orientation_ == 2) { | ||||
|       for (uint8_t i = 0; i < 8; i++) { | ||||
|         if (this->flip_x_) { | ||||
|           b |= ((pixels[i] >> (7 - col)) & 1) << (7 - i); | ||||
|         } else { | ||||
|           b |= ((pixels[i] >> (7 - col)) & 1) << i; | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       b = pixels[7 - col]; | ||||
|     } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user