Bump bleak-esphome to 2.9.0 (#139467)

* Bump bleak-esphome to 2.9.0

changelog: https://github.com/Bluetooth-Devices/bleak-esphome/compare/v2.8.0...v2.9.0

* fixes
This commit is contained in:
J. Nick Koston 2025-02-28 22:17:44 +00:00 committed by GitHub
parent db05aa17d3
commit ee1fe2cae4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 131 additions and 26 deletions

View File

@ -22,5 +22,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["eq3btsmart"],
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.8.0"]
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.9.0"]
}

View File

@ -16,7 +16,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import CONF_NOISE_PSK, DATA_FFMPEG_PROXY, DOMAIN
from .const import CONF_BLUETOOTH_MAC_ADDRESS, CONF_NOISE_PSK, DATA_FFMPEG_PROXY, DOMAIN
from .dashboard import async_setup as async_setup_dashboard
from .domain_data import DomainData
@ -87,6 +87,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ESPHomeConfigEntry) ->
async def async_remove_entry(hass: HomeAssistant, entry: ESPHomeConfigEntry) -> None:
"""Remove an esphome config entry."""
if mac_address := entry.unique_id:
async_remove_scanner(hass, mac_address.upper())
if bluetooth_mac_address := entry.data.get(CONF_BLUETOOTH_MAC_ADDRESS):
async_remove_scanner(hass, bluetooth_mac_address.upper())
await DomainData.get(hass).get_or_create_store(hass, entry).async_remove()

View File

@ -8,6 +8,7 @@ CONF_ALLOW_SERVICE_CALLS = "allow_service_calls"
CONF_SUBSCRIBE_LOGS = "subscribe_logs"
CONF_DEVICE_NAME = "device_name"
CONF_NOISE_PSK = "noise_psk"
CONF_BLUETOOTH_MAC_ADDRESS = "bluetooth_mac_address"
DEFAULT_ALLOW_SERVICE_CALLS = True
DEFAULT_NEW_CONFIG_ALLOW_ALLOW_SERVICE_CALLS = False

View File

@ -25,13 +25,17 @@ async def async_get_config_entry_diagnostics(
diag["config"] = config_entry.as_dict()
entry_data = config_entry.runtime_data
device_info = entry_data.device_info
if (storage_data := await entry_data.store.async_load()) is not None:
diag["storage_data"] = storage_data
if (
config_entry.unique_id
and (scanner := async_scanner_by_source(hass, config_entry.unique_id.upper()))
device_info
and (
scanner_mac := device_info.bluetooth_mac_address or device_info.mac_address
)
and (scanner := async_scanner_by_source(hass, scanner_mac.upper()))
and (bluetooth_device := entry_data.bluetooth_device)
):
diag["bluetooth"] = {

View File

@ -63,6 +63,7 @@ from homeassistant.util.async_ import create_eager_task
from .bluetooth import async_connect_scanner
from .const import (
CONF_ALLOW_SERVICE_CALLS,
CONF_BLUETOOTH_MAC_ADDRESS,
CONF_DEVICE_NAME,
CONF_SUBSCRIBE_LOGS,
DEFAULT_ALLOW_SERVICE_CALLS,
@ -431,6 +432,13 @@ class ESPHomeManager:
device_mac = format_mac(device_info.mac_address)
mac_address_matches = unique_id == device_mac
if (
bluetooth_mac_address := device_info.bluetooth_mac_address
) and entry.data.get(CONF_BLUETOOTH_MAC_ADDRESS) != bluetooth_mac_address:
hass.config_entries.async_update_entry(
entry,
data={**entry.data, CONF_BLUETOOTH_MAC_ADDRESS: bluetooth_mac_address},
)
#
# Migrate config entry to new unique ID if the current
# unique id is not a mac address.
@ -498,7 +506,9 @@ class ESPHomeManager:
)
)
else:
bluetooth.async_remove_scanner(hass, device_info.mac_address)
bluetooth.async_remove_scanner(
hass, device_info.bluetooth_mac_address or device_info.mac_address
)
if device_info.voice_assistant_feature_flags_compat(api_version) and (
Platform.ASSIST_SATELLITE not in entry_data.loaded_platforms
@ -617,11 +627,22 @@ class ESPHomeManager:
)
_setup_services(hass, entry_data, services)
if entry_data.device_info is not None and entry_data.device_info.name:
reconnect_logic.name = entry_data.device_info.name
if (device_info := entry_data.device_info) is not None:
if device_info.name:
reconnect_logic.name = device_info.name
if (
bluetooth_mac_address := device_info.bluetooth_mac_address
) and entry.data.get(CONF_BLUETOOTH_MAC_ADDRESS) != bluetooth_mac_address:
hass.config_entries.async_update_entry(
entry,
data={
**entry.data,
CONF_BLUETOOTH_MAC_ADDRESS: bluetooth_mac_address,
},
)
if entry.unique_id is None:
hass.config_entries.async_update_entry(
entry, unique_id=format_mac(entry_data.device_info.mac_address)
entry, unique_id=format_mac(device_info.mac_address)
)
await reconnect_logic.start()

View File

@ -18,7 +18,7 @@
"requirements": [
"aioesphomeapi==29.3.1",
"esphome-dashboard-api==1.2.3",
"bleak-esphome==2.8.0"
"bleak-esphome==2.9.0"
],
"zeroconf": ["_esphomelib._tcp.local."]
}

2
requirements_all.txt generated
View File

@ -603,7 +603,7 @@ bizkaibus==0.1.1
# homeassistant.components.eq3btsmart
# homeassistant.components.esphome
bleak-esphome==2.8.0
bleak-esphome==2.9.0
# homeassistant.components.bluetooth
bleak-retry-connector==3.9.0

View File

@ -534,7 +534,7 @@ bimmer-connected[china]==0.17.2
# homeassistant.components.eq3btsmart
# homeassistant.components.esphome
bleak-esphome==2.8.0
bleak-esphome==2.9.0
# homeassistant.components.bluetooth
bleak-retry-connector==3.9.0

View File

@ -30,6 +30,7 @@ from zeroconf import Zeroconf
from homeassistant.components.esphome import dashboard
from homeassistant.components.esphome.const import (
CONF_ALLOW_SERVICE_CALLS,
CONF_BLUETOOTH_MAC_ADDRESS,
CONF_DEVICE_NAME,
CONF_NOISE_PSK,
DEFAULT_NEW_CONFIG_ALLOW_ALLOW_SERVICE_CALLS,
@ -578,6 +579,19 @@ async def mock_bluetooth_entry(
async def _mock_bluetooth_entry(
bluetooth_proxy_feature_flags: BluetoothProxyFeature,
) -> MockESPHomeDevice:
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_HOST: "test.local",
CONF_PORT: 6053,
CONF_PASSWORD: "",
CONF_BLUETOOTH_MAC_ADDRESS: "AA:BB:CC:DD:EE:FC",
},
options={
CONF_ALLOW_SERVICE_CALLS: DEFAULT_NEW_CONFIG_ALLOW_ALLOW_SERVICE_CALLS
},
)
entry.add_to_hass(hass)
return await _mock_generic_device_entry(
hass,
mock_client,
@ -587,6 +601,7 @@ async def mock_bluetooth_entry(
},
([], []),
[],
entry=entry,
)
return _mock_bluetooth_entry

View File

@ -13,7 +13,7 @@ async def test_bluetooth_connect_with_raw_adv(
hass: HomeAssistant, mock_bluetooth_entry_with_raw_adv: MockESPHomeDevice
) -> None:
"""Test bluetooth connect with raw advertisements."""
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner is not None
assert scanner.connectable is True
assert scanner.scanning is True
@ -21,11 +21,11 @@ async def test_bluetooth_connect_with_raw_adv(
await mock_bluetooth_entry_with_raw_adv.mock_disconnect(True)
await hass.async_block_till_done()
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner is None
await mock_bluetooth_entry_with_raw_adv.mock_connect()
await hass.async_block_till_done()
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner.scanning is True
@ -33,7 +33,7 @@ async def test_bluetooth_connect_with_legacy_adv(
hass: HomeAssistant, mock_bluetooth_entry_with_legacy_adv: MockESPHomeDevice
) -> None:
"""Test bluetooth connect with legacy advertisements."""
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner is not None
assert scanner.connectable is True
assert scanner.scanning is True
@ -41,11 +41,11 @@ async def test_bluetooth_connect_with_legacy_adv(
await mock_bluetooth_entry_with_legacy_adv.mock_disconnect(True)
await hass.async_block_till_done()
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner is None
await mock_bluetooth_entry_with_legacy_adv.mock_connect()
await hass.async_block_till_done()
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner.scanning is True
@ -55,10 +55,10 @@ async def test_bluetooth_device_linked_via_device(
device_registry: dr.DeviceRegistry,
) -> None:
"""Test the Bluetooth device is linked to the ESPHome device."""
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner.connectable is True
entry = hass.config_entries.async_entry_for_domain_unique_id(
"bluetooth", "11:22:33:44:55:AA"
"bluetooth", "AA:BB:CC:DD:EE:FC"
)
assert entry is not None
esp_device = device_registry.async_get_device(
@ -71,7 +71,7 @@ async def test_bluetooth_device_linked_via_device(
)
assert esp_device is not None
device = device_registry.async_get_device(
connections={(dr.CONNECTION_BLUETOOTH, "11:22:33:44:55:AA")}
connections={(dr.CONNECTION_BLUETOOTH, "AA:BB:CC:DD:EE:FC")}
)
assert device is not None
assert device.via_device_id == esp_device.id
@ -81,7 +81,7 @@ async def test_bluetooth_cleanup_on_remove_entry(
hass: HomeAssistant, mock_bluetooth_entry_with_raw_adv: MockESPHomeDevice
) -> None:
"""Test bluetooth is cleaned up on entry removal."""
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner.connectable is True
await hass.config_entries.async_unload(
mock_bluetooth_entry_with_raw_adv.entry.entry_id

View File

@ -37,7 +37,7 @@ async def test_diagnostics_with_bluetooth(
mock_bluetooth_entry_with_raw_adv: MockESPHomeDevice,
) -> None:
"""Test diagnostics for config entry with Bluetooth."""
scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA")
scanner = bluetooth.async_scanner_by_source(hass, "AA:BB:CC:DD:EE:FC")
assert scanner is not None
assert scanner.connectable is True
entry = mock_bluetooth_entry_with_raw_adv.entry
@ -55,9 +55,9 @@ async def test_diagnostics_with_bluetooth(
"discovered_devices_and_advertisement_data": [],
"last_detection": ANY,
"monotonic_time": ANY,
"name": "test (11:22:33:44:55:AA)",
"name": "test (AA:BB:CC:DD:EE:FC)",
"scanning": True,
"source": "11:22:33:44:55:AA",
"source": "AA:BB:CC:DD:EE:FC",
"start_time": ANY,
"time_since_last_device_detection": {},
"type": "ESPHomeScanner",
@ -66,6 +66,7 @@ async def test_diagnostics_with_bluetooth(
"config": {
"created_at": ANY,
"data": {
"bluetooth_mac_address": "**REDACTED**",
"device_name": "test",
"host": "test.local",
"password": "",

View File

@ -25,6 +25,7 @@ import pytest
from homeassistant import config_entries
from homeassistant.components.esphome.const import (
CONF_ALLOW_SERVICE_CALLS,
CONF_BLUETOOTH_MAC_ADDRESS,
CONF_DEVICE_NAME,
CONF_SUBSCRIBE_LOGS,
DOMAIN,
@ -475,6 +476,39 @@ async def test_unique_id_updated_to_mac(hass: HomeAssistant, mock_client) -> Non
assert entry.unique_id == "11:22:33:44:55:aa"
@pytest.mark.usefixtures("mock_zeroconf")
async def test_add_missing_bluetooth_mac_address(
hass: HomeAssistant, mock_client
) -> None:
"""Test bluetooth mac is added if its missing."""
entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: "test.local", CONF_PORT: 6053, CONF_PASSWORD: ""},
unique_id="mock-config-name",
)
entry.add_to_hass(hass)
subscribe_done = hass.loop.create_future()
def async_subscribe_states(*args, **kwargs) -> None:
subscribe_done.set_result(None)
mock_client.subscribe_states = async_subscribe_states
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(
mac_address="1122334455aa",
bluetooth_mac_address="AA:BB:CC:DD:EE:FF",
)
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
async with asyncio.timeout(1):
await subscribe_done
assert entry.unique_id == "11:22:33:44:55:aa"
assert entry.data.get(CONF_BLUETOOTH_MAC_ADDRESS) == "AA:BB:CC:DD:EE:FF"
@pytest.mark.usefixtures("mock_zeroconf")
async def test_unique_id_not_updated_if_name_same_and_already_mac(
hass: HomeAssistant, mock_client: APIClient
@ -1337,3 +1371,32 @@ async def test_entry_missing_unique_id(
await mock_esphome_device(mock_client=mock_client, mock_storage=True)
await hass.async_block_till_done()
assert entry.unique_id == "11:22:33:44:55:aa"
async def test_entry_missing_bluetooth_mac_address(
hass: HomeAssistant,
mock_client: APIClient,
mock_esphome_device: Callable[
[APIClient, list[EntityInfo], list[UserService], list[EntityState]],
Awaitable[MockESPHomeDevice],
],
) -> None:
"""Test the bluetooth_mac_address is added if available."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id=None,
data={
CONF_HOST: "test.local",
CONF_PORT: 6053,
CONF_PASSWORD: "",
},
options={CONF_ALLOW_SERVICE_CALLS: True},
)
entry.add_to_hass(hass)
await mock_esphome_device(
mock_client=mock_client,
mock_storage=True,
device_info={"bluetooth_mac_address": "AA:BB:CC:DD:EE:FC"},
)
await hass.async_block_till_done()
assert entry.data[CONF_BLUETOOTH_MAC_ADDRESS] == "AA:BB:CC:DD:EE:FC"