Skip to content

Commit

Permalink
2024.5.5 (#118049)
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck committed May 24, 2024
2 parents 3dc3de9 + 750ec26 commit c347311
Show file tree
Hide file tree
Showing 40 changed files with 705 additions and 67 deletions.
3 changes: 3 additions & 0 deletions homeassistant/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ async def async_from_config_dict(
start = monotonic()

hass.config_entries = config_entries.ConfigEntries(hass, config)
# Prime custom component cache early so we know if registry entries are tied
# to a custom integration
await loader.async_get_custom_components(hass)
await async_load_base_functionality(hass)

# Set up core.
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/aranet/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
"documentation": "https://www.home-assistant.io/integrations/aranet",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["aranet4==2.3.3"]
"requirements": ["aranet4==2.3.4"]
}
4 changes: 2 additions & 2 deletions homeassistant/components/crownstone/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"crownstone_uart"
],
"requirements": [
"crownstone-cloud==1.4.9",
"crownstone-sse==2.0.4",
"crownstone-cloud==1.4.11",
"crownstone-sse==2.0.5",
"crownstone-uart==2.1.0",
"pyserial==3.5"
]
Expand Down
18 changes: 10 additions & 8 deletions homeassistant/components/homeassistant_sky_connect/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ def get_hardware_variant(config_entry: ConfigEntry) -> HardwareVariant:
return HardwareVariant.from_usb_product_name(config_entry.data["product"])


def get_zha_device_path(config_entry: ConfigEntry) -> str:
def get_zha_device_path(config_entry: ConfigEntry) -> str | None:
"""Get the device path from a ZHA config entry."""
return cast(str, config_entry.data["device"]["path"])
return cast(str | None, config_entry.data.get("device", {}).get("path", None))


@singleton(OTBR_ADDON_MANAGER_DATA)
Expand Down Expand Up @@ -94,13 +94,15 @@ async def guess_firmware_type(hass: HomeAssistant, device_path: str) -> Firmware

for zha_config_entry in hass.config_entries.async_entries(ZHA_DOMAIN):
zha_path = get_zha_device_path(zha_config_entry)
device_guesses[zha_path].append(
FirmwareGuess(
is_running=(zha_config_entry.state == ConfigEntryState.LOADED),
firmware_type=ApplicationType.EZSP,
source="zha",

if zha_path is not None:
device_guesses[zha_path].append(
FirmwareGuess(
is_running=(zha_config_entry.state == ConfigEntryState.LOADED),
firmware_type=ApplicationType.EZSP,
source="zha",
)
)
)

if is_hassio(hass):
otbr_addon_manager = get_otbr_addon_manager(hass)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/huawei_lte/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def format_default(value: StateType) -> tuple[StateType, str | None]:
if value is not None:
# Clean up value and infer unit, e.g. -71dBm, 15 dB
if match := re.match(
r"([>=<]*)(?P<value>.+?)\s*(?P<unit>[a-zA-Z]+)\s*$", str(value)
r"((&[gl]t;|[><])=?)?(?P<value>.+?)\s*(?P<unit>[a-zA-Z]+)\s*$", str(value)
):
try:
value = float(match.group("value"))
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/matter/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@
# sw version (attributeKey 0/40/10)
TRANSITION_BLOCKLIST = (
(4488, 514, "1.0", "1.0.0"),
(4488, 260, "1.0", "1.0.0"),
(5010, 769, "3.0", "1.0.0"),
(4999, 25057, "1.0", "27.0"),
(4448, 36866, "V1", "V1.0.0.5"),
)


Expand Down
8 changes: 8 additions & 0 deletions homeassistant/components/mqtt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,14 @@ def _async_start_misc_loop(self) -> None:

def _increase_socket_buffer_size(self, sock: SocketType) -> None:
"""Increase the socket buffer size."""
if not hasattr(sock, "setsockopt") and hasattr(sock, "_socket"):
# The WebsocketWrapper does not wrap setsockopt
# so we need to get the underlying socket
# Remove this once
# https://github.com/eclipse/paho.mqtt.python/pull/843
# is available.
sock = sock._socket # pylint: disable=protected-access

new_buffer_size = PREFERRED_BUFFER_SIZE
while True:
try:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/philips_js/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/philips_js",
"iot_class": "local_polling",
"loggers": ["haphilipsjs"],
"requirements": ["ha-philipsjs==3.1.1"]
"requirements": ["ha-philipsjs==3.2.1"]
}
6 changes: 5 additions & 1 deletion homeassistant/components/plugwise/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,17 @@ def __init__(
super().__init__(coordinator, device_id)
self.entity_description = entity_description
self._attr_unique_id = f"{device_id}-{entity_description.key}"
self._attr_options = self.device[entity_description.options_key]

@property
def current_option(self) -> str:
"""Return the selected entity option to represent the entity state."""
return self.device[self.entity_description.key]

@property
def options(self) -> list[str]:
"""Return the available select-options."""
return self.device[self.entity_description.options_key]

async def async_select_option(self, option: str) -> None:
"""Change to the selected entity option."""
await self.entity_description.command(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/risco/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"iot_class": "local_push",
"loggers": ["pyrisco"],
"quality_scale": "platinum",
"requirements": ["pyrisco==0.6.1"]
"requirements": ["pyrisco==0.6.2"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/rympro/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/rympro",
"iot_class": "cloud_polling",
"requirements": ["pyrympro==0.0.7"]
"requirements": ["pyrympro==0.0.8"]
}
1 change: 1 addition & 0 deletions homeassistant/components/shelly/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ async def _async_discovered_mac(self, mac: str, host: str) -> None:
if (
current_entry := await self.async_set_unique_id(mac)
) and current_entry.data.get(CONF_HOST) == host:
LOGGER.debug("async_reconnect_soon: host: %s, mac: %s", host, mac)
await async_reconnect_soon(self.hass, current_entry)
if host == INTERNAL_WIFI_AP_IP:
# If the device is broadcasting the internal wifi ap ip
Expand Down
14 changes: 8 additions & 6 deletions homeassistant/components/sonos/media_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ def get_thumbnail_url_full(
media_content_type: str,
media_content_id: str,
media_image_id: str | None = None,
item: MusicServiceItem | None = None,
) -> str | None:
"""Get thumbnail URL."""
if is_internal:
item = get_media(
media.library,
media_content_id,
media_content_type,
)
if not item:
item = get_media(
media.library,
media_content_id,
media_content_type,
)
return urllib.parse.unquote(getattr(item, "album_art_uri", ""))

return urllib.parse.unquote(
Expand Down Expand Up @@ -255,7 +257,7 @@ def item_payload(item: DidlObject, get_thumbnail_url=None) -> BrowseMedia:
content_id = get_content_id(item)
thumbnail = None
if getattr(item, "album_art_uri", None):
thumbnail = get_thumbnail_url(media_class, content_id)
thumbnail = get_thumbnail_url(media_class, content_id, item=item)

return BrowseMedia(
title=item.title,
Expand Down
15 changes: 11 additions & 4 deletions homeassistant/components/switchbot/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from switchbot import (
SwitchbotAccountConnectionError,
SwitchBotAdvertisement,
SwitchbotApiError,
SwitchbotAuthenticationError,
SwitchbotLock,
SwitchbotModel,
Expand All @@ -33,6 +34,7 @@
)
from homeassistant.core import callback
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import (
CONF_ENCRYPTION_KEY,
Expand Down Expand Up @@ -175,14 +177,19 @@ async def async_step_lock_auth(
description_placeholders = {}
if user_input is not None:
try:
key_details = await self.hass.async_add_executor_job(
SwitchbotLock.retrieve_encryption_key,
key_details = await SwitchbotLock.async_retrieve_encryption_key(
async_get_clientsession(self.hass),
self._discovered_adv.address,
user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
)
except SwitchbotAccountConnectionError as ex:
raise AbortFlow("cannot_connect") from ex
except (SwitchbotApiError, SwitchbotAccountConnectionError) as ex:
_LOGGER.debug(
"Failed to connect to SwitchBot API: %s", ex, exc_info=True
)
raise AbortFlow(
"api_error", description_placeholders={"error_detail": str(ex)}
) from ex
except SwitchbotAuthenticationError as ex:
_LOGGER.debug("Authentication failed: %s", ex, exc_info=True)
errors = {"base": "auth_failed"}
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/switchbot/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@
"documentation": "https://www.home-assistant.io/integrations/switchbot",
"iot_class": "local_push",
"loggers": ["switchbot"],
"requirements": ["PySwitchbot==0.45.0"]
"requirements": ["PySwitchbot==0.46.1"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/switchbot/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"already_configured_device": "[%key:common::config_flow::abort::already_configured_device%]",
"no_devices_found": "No supported SwitchBot devices found in range; If the device is in range, ensure the scanner has active scanning enabled, as SwitchBot devices cannot be discovered with passive scans. Active scans can be disabled once the device is configured. If you need clarification on whether the device is in-range, download the diagnostics for the integration that provides your Bluetooth adapter or proxy and check if the MAC address of the SwitchBot device is present.",
"unknown": "[%key:common::config_flow::error::unknown%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"api_error": "Error while communicating with SwitchBot API: {error_detail}",
"switchbot_unsupported_type": "Unsupported Switchbot Type."
}
},
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/synology_dsm/media_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@

async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
"""Set up Synology media source."""
entries = hass.config_entries.async_entries(DOMAIN)
entries = hass.config_entries.async_entries(
DOMAIN, include_disabled=False, include_ignore=False
)
hass.http.register_view(SynologyDsmMediaView(hass))
return SynologyPhotosMediaSource(hass, entries)

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/tesla_wall_connector/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"not_connected": "Vehicle not connected",
"connected": "Vehicle connected",
"ready": "Ready to charge",
"negociating": "Negociating connection",
"negotiating": "Negotiating connection",
"error": "Error",
"charging_finished": "Charging finished",
"waiting_car": "Waiting for car",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/wled/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_push",
"quality_scale": "platinum",
"requirements": ["wled==0.17.1"],
"requirements": ["wled==0.18.0"],
"zeroconf": ["_wled._tcp.local."]
}
20 changes: 20 additions & 0 deletions homeassistant/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,15 @@ async def async_setup(

# Only store setup result as state if it was not forwarded.
if domain_is_integration := self.domain == integration.domain:
if self.state in (
ConfigEntryState.LOADED,
ConfigEntryState.SETUP_IN_PROGRESS,
):
raise OperationNotAllowed(
f"The config entry {self.title} ({self.domain}) with entry_id"
f" {self.entry_id} cannot be setup because is already loaded in the"
f" {self.state} state"
)
self._async_set_state(hass, ConfigEntryState.SETUP_IN_PROGRESS, None)

if self.supports_unload is None:
Expand Down Expand Up @@ -709,6 +718,17 @@ async def async_setup_locked(
) -> None:
"""Set up while holding the setup lock."""
async with self.setup_lock:
if self.state is ConfigEntryState.LOADED:
# If something loaded the config entry while
# we were waiting for the lock, we should not
# set it up again.
_LOGGER.debug(
"Not setting up %s (%s %s) again, already loaded",
self.title,
self.domain,
self.entry_id,
)
return
await self.async_setup(hass, integration=integration)

@callback
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2024
MINOR_VERSION: Final = 5
PATCH_VERSION: Final = "4"
PATCH_VERSION: Final = "5"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/helpers/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -1766,14 +1766,14 @@ def _pattern_time_change_listener(self, _: datetime) -> None:
# time when the timer was scheduled
utc_now = time_tracker_utcnow()
localized_now = dt_util.as_local(utc_now) if self.local else utc_now
hass.async_run_hass_job(self.job, localized_now, background=True)
if TYPE_CHECKING:
assert self._pattern_time_change_listener_job is not None
self._cancel_callback = async_track_point_in_utc_time(
hass,
self._pattern_time_change_listener_job,
self._calculate_next(utc_now + timedelta(seconds=1)),
)
hass.async_run_hass_job(self.job, localized_now, background=True)

@callback
def async_cancel(self) -> None:
Expand Down
13 changes: 13 additions & 0 deletions homeassistant/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ class BlockedIntegration:
"dreame_vacuum": BlockedIntegration(
AwesomeVersion("1.0.4"), "crashes Home Assistant"
),
# Added in 2024.5.5 because of
# https://github.com/sh00t2kill/dolphin-robot/issues/185
"mydolphin_plus": BlockedIntegration(
AwesomeVersion("1.0.13"), "crashes Home Assistant"
),
}

DATA_COMPONENTS = "components"
Expand Down Expand Up @@ -1669,6 +1674,14 @@ def async_get_issue_tracker(
# If we know nothing about the entity, suggest opening an issue on HA core
return issue_tracker

if (
not integration
and (hass and integration_domain)
and (comps_or_future := hass.data.get(DATA_CUSTOM_COMPONENTS))
and not isinstance(comps_or_future, asyncio.Future)
):
integration = comps_or_future.get(integration_domain)

if not integration and (hass and integration_domain):
with suppress(IntegrationNotLoaded):
integration = async_get_loaded_integration(hass, integration_domain)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "homeassistant"
version = "2024.5.4"
version = "2024.5.5"
license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3."
readme = "README.rst"
Expand Down
Loading

0 comments on commit c347311

Please sign in to comment.