diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index a446d54a4fe84b..55efee86bfb30c 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -326,7 +326,7 @@ jobs: uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install Cosign - uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 + uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 with: cosign-release: "v2.2.3" diff --git a/CODEOWNERS b/CODEOWNERS index 6ffc83f6bf300e..27a93ee9094594 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -619,6 +619,8 @@ build.json @home-assistant/supervisor /tests/components/greeneye_monitor/ @jkeljo /homeassistant/components/group/ @home-assistant/core /tests/components/group/ @home-assistant/core +/homeassistant/components/growatt_server/ @johanzander +/tests/components/growatt_server/ @johanzander /homeassistant/components/guardian/ @bachya /tests/components/guardian/ @bachya /homeassistant/components/habitica/ @tr4nt0r diff --git a/homeassistant/components/airq/strings.json b/homeassistant/components/airq/strings.json index 2972ba5c15bc4c..e38c5b9fdfe95b 100644 --- a/homeassistant/components/airq/strings.json +++ b/homeassistant/components/airq/strings.json @@ -29,7 +29,7 @@ }, "data_description": { "return_average": "air-Q allows to poll both the noisy sensor readings as well as the values averaged on the device (default)", - "clip_negatives": "For baseline calibration purposes, certain sensor values may briefly become negative. The default behaviour is to clip such values to 0" + "clip_negatives": "For baseline calibration purposes, certain sensor values may briefly become negative. The default behavior is to clip such values to 0" } } } diff --git a/homeassistant/components/bayesian/strings.json b/homeassistant/components/bayesian/strings.json index 7204c867623c82..8bfd2642f3d81c 100644 --- a/homeassistant/components/bayesian/strings.json +++ b/homeassistant/components/bayesian/strings.json @@ -146,7 +146,7 @@ }, "state": { "title": "Add a Bayesian sensor", - "description": "Add an observation which evaluates to `True` when the value of the sensor exactly matches *'To state'*. When `False`, it will update the prior with probabilities that are the inverse of those set below. This behaviour can be overridden by adding observations for the same entity's other states.", + "description": "Add an observation which evaluates to `True` when the value of the sensor exactly matches *'To state'*. When `False`, it will update the prior with probabilities that are the inverse of those set below. This behavior can be overridden by adding observations for the same entity's other states.", "data": { "name": "[%key:common::config_flow::data::name%]", diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index d4edd0d34f21ca..941a7822439e01 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -113,7 +113,6 @@ "BluetoothServiceInfo", "BluetoothServiceInfoBleak", "HaBluetoothConnector", - "HomeAssistantRemoteScanner", "async_address_present", "async_ble_device_from_address", "async_clear_address_from_match_history", diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index 62496906c9de88..3baea0f5b2e3f2 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -12,7 +12,9 @@ from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.google_assistant import DOMAIN as GOOGLE_DOMAIN -from homeassistant.components.google_assistant.helpers import AbstractConfig +from homeassistant.components.google_assistant.helpers import ( # pylint: disable=hass-component-root-import + AbstractConfig, +) from homeassistant.components.homeassistant.exposed_entities import ( async_expose_entity, async_get_assistant_settings, diff --git a/homeassistant/components/cloud/prefs.py b/homeassistant/components/cloud/prefs.py index ae4b2794e1b24e..412c0cf75a8e17 100644 --- a/homeassistant/components/cloud/prefs.py +++ b/homeassistant/components/cloud/prefs.py @@ -11,7 +11,7 @@ from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.auth.models import User from homeassistant.components import webhook -from homeassistant.components.google_assistant.http import ( +from homeassistant.components.google_assistant.http import ( # pylint: disable=hass-component-root-import async_get_users as async_get_google_assistant_users, ) from homeassistant.core import HomeAssistant, callback diff --git a/homeassistant/components/config/automation.py b/homeassistant/components/config/automation.py index f2646aa5451b27..50148bc88ae3fa 100644 --- a/homeassistant/components/config/automation.py +++ b/homeassistant/components/config/automation.py @@ -6,7 +6,9 @@ import uuid from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN -from homeassistant.components.automation.config import async_validate_config_item +from homeassistant.components.automation.config import ( # pylint: disable=hass-component-root-import + async_validate_config_item, +) from homeassistant.config import AUTOMATION_CONFIG_PATH from homeassistant.const import CONF_ID, SERVICE_RELOAD from homeassistant.core import HomeAssistant, callback diff --git a/homeassistant/components/config/script.py b/homeassistant/components/config/script.py index aa83329d124c6a..7e18e926c7f115 100644 --- a/homeassistant/components/config/script.py +++ b/homeassistant/components/config/script.py @@ -5,7 +5,9 @@ from typing import Any from homeassistant.components.script import DOMAIN as SCRIPT_DOMAIN -from homeassistant.components.script.config import async_validate_config_item +from homeassistant.components.script.config import ( # pylint: disable=hass-component-root-import + async_validate_config_item, +) from homeassistant.config import SCRIPT_CONFIG_PATH from homeassistant.const import SERVICE_RELOAD from homeassistant.core import HomeAssistant, callback diff --git a/homeassistant/components/ffmpeg_noise/binary_sensor.py b/homeassistant/components/ffmpeg_noise/binary_sensor.py index 1623d7c7660772..cc6f20cde7f414 100644 --- a/homeassistant/components/ffmpeg_noise/binary_sensor.py +++ b/homeassistant/components/ffmpeg_noise/binary_sensor.py @@ -19,7 +19,9 @@ FFmpegManager, get_ffmpeg_manager, ) -from homeassistant.components.ffmpeg_motion.binary_sensor import FFmpegBinarySensor +from homeassistant.components.ffmpeg_motion.binary_sensor import ( # pylint: disable=hass-component-root-import + FFmpegBinarySensor, +) from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv diff --git a/homeassistant/components/freebox/camera.py b/homeassistant/components/freebox/camera.py index f7e078f073695d..ea352416f37196 100644 --- a/homeassistant/components/freebox/camera.py +++ b/homeassistant/components/freebox/camera.py @@ -6,9 +6,8 @@ from typing import Any from homeassistant.components.camera import CameraEntityFeature -from homeassistant.components.ffmpeg.camera import ( - CONF_EXTRA_ARGUMENTS, - CONF_INPUT, +from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, CONF_INPUT +from homeassistant.components.ffmpeg.camera import ( # pylint: disable=hass-component-root-import DEFAULT_ARGUMENTS, FFmpegCamera, ) diff --git a/homeassistant/components/growatt_server/manifest.json b/homeassistant/components/growatt_server/manifest.json index b6a730835bbb8f..45dc93d2444c9a 100644 --- a/homeassistant/components/growatt_server/manifest.json +++ b/homeassistant/components/growatt_server/manifest.json @@ -1,7 +1,7 @@ { "domain": "growatt_server", "name": "Growatt", - "codeowners": [], + "codeowners": ["@johanzander"], "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/growatt_server", "iot_class": "cloud_polling", diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 7c132a00a77835..2d4ebff955bebd 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -27,7 +27,7 @@ BinarySensorDeviceClass, ) from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN -from homeassistant.components.device_automation.trigger import ( +from homeassistant.components.device_automation.trigger import ( # pylint: disable=hass-component-root-import async_validate_trigger_config, ) from homeassistant.components.event import DOMAIN as EVENT_DOMAIN, EventDeviceClass diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json index d587e02e1a524d..df7ddc86e8c443 100644 --- a/homeassistant/components/knx/strings.json +++ b/homeassistant/components/knx/strings.json @@ -358,7 +358,7 @@ "entity_label": "Entity name", "entity_description": "Optional if a device is selected, otherwise required. If the entity is assigned to a device, the device name is used as prefix.", "entity_category_title": "Entity category", - "entity_category_description": "Classification of a non-primary entity. Leave empty for standard behaviour." + "entity_category_description": "Classification of a non-primary entity. Leave empty for standard behavior." }, "knx": { "title": "KNX configuration", diff --git a/homeassistant/components/mealie/manifest.json b/homeassistant/components/mealie/manifest.json index 2fae62f27cd260..ffb52716ba8293 100644 --- a/homeassistant/components/mealie/manifest.json +++ b/homeassistant/components/mealie/manifest.json @@ -7,5 +7,5 @@ "integration_type": "service", "iot_class": "local_polling", "quality_scale": "platinum", - "requirements": ["aiomealie==1.0.0"] + "requirements": ["aiomealie==1.0.1"] } diff --git a/homeassistant/components/metoffice/__init__.py b/homeassistant/components/metoffice/__init__.py index 913d87fe3d7cfa..352d7f11f96d24 100644 --- a/homeassistant/components/metoffice/__init__.py +++ b/homeassistant/components/metoffice/__init__.py @@ -5,9 +5,8 @@ import asyncio import logging -import datapoint -import datapoint.Forecast -import datapoint.Manager +from datapoint.Forecast import Forecast +from datapoint.Manager import Manager from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -48,19 +47,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: coordinates = f"{latitude}_{longitude}" - connection = datapoint.Manager.Manager(api_key=api_key) + connection = Manager(api_key=api_key) - async def async_update_hourly() -> datapoint.Forecast: + async def async_update_hourly() -> Forecast: return await hass.async_add_executor_job( fetch_data, connection, latitude, longitude, "hourly" ) - async def async_update_daily() -> datapoint.Forecast: + async def async_update_daily() -> Forecast: return await hass.async_add_executor_job( fetch_data, connection, latitude, longitude, "daily" ) - async def async_update_twice_daily() -> datapoint.Forecast: + async def async_update_twice_daily() -> Forecast: return await hass.async_add_executor_job( fetch_data, connection, latitude, longitude, "twice-daily" ) diff --git a/homeassistant/components/metoffice/config_flow.py b/homeassistant/components/metoffice/config_flow.py index 81369daf09a6c4..19da754fc6a245 100644 --- a/homeassistant/components/metoffice/config_flow.py +++ b/homeassistant/components/metoffice/config_flow.py @@ -6,9 +6,8 @@ import logging from typing import Any -import datapoint from datapoint.exceptions import APIException -import datapoint.Manager +from datapoint.Manager import Manager from requests import HTTPError import voluptuous as vol @@ -31,7 +30,7 @@ async def validate_input( Data has the keys from DATA_SCHEMA with values provided by the user. """ errors = {} - connection = datapoint.Manager.Manager(api_key=api_key) + connection = Manager(api_key=api_key) try: forecast = await hass.async_add_executor_job( diff --git a/homeassistant/components/metoffice/helpers.py b/homeassistant/components/metoffice/helpers.py index e6bb8a34020a0d..512faffafb4b52 100644 --- a/homeassistant/components/metoffice/helpers.py +++ b/homeassistant/components/metoffice/helpers.py @@ -5,8 +5,9 @@ import logging from typing import Any, Literal -import datapoint +from datapoint.exceptions import APIException from datapoint.Forecast import Forecast +from datapoint.Manager import Manager from requests import HTTPError from homeassistant.exceptions import ConfigEntryAuthFailed @@ -16,7 +17,7 @@ def fetch_data( - connection: datapoint.Manager, + connection: Manager, latitude: float, longitude: float, frequency: Literal["daily", "twice-daily", "hourly"], @@ -26,7 +27,7 @@ def fetch_data( return connection.get_forecast( latitude, longitude, frequency, convert_weather_code=False ) - except (ValueError, datapoint.exceptions.APIException) as err: + except (ValueError, APIException) as err: _LOGGER.error("Check Met Office connection: %s", err.args) raise UpdateFailed from err except HTTPError as err: diff --git a/homeassistant/components/metoffice/weather.py b/homeassistant/components/metoffice/weather.py index 90fbc36f8fb9df..04a2c0fd5b8526 100644 --- a/homeassistant/components/metoffice/weather.py +++ b/homeassistant/components/metoffice/weather.py @@ -5,7 +5,7 @@ from datetime import datetime from typing import Any, cast -from datapoint.Forecast import Forecast as ForecastData +from datapoint.Forecast import Forecast from homeassistant.components.weather import ( ATTR_FORECAST_CONDITION, @@ -22,7 +22,7 @@ ATTR_FORECAST_WIND_BEARING, DOMAIN as WEATHER_DOMAIN, CoordinatorWeatherEntity, - Forecast, + Forecast as WeatherForecast, WeatherEntityFeature, ) from homeassistant.config_entries import ConfigEntry @@ -85,20 +85,20 @@ async def async_setup_entry( ) -def _build_hourly_forecast_data(timestep: dict[str, Any]) -> Forecast: - data = Forecast(datetime=timestep["time"].isoformat()) +def _build_hourly_forecast_data(timestep: dict[str, Any]) -> WeatherForecast: + data = WeatherForecast(datetime=timestep["time"].isoformat()) _populate_forecast_data(data, timestep, HOURLY_FORECAST_ATTRIBUTE_MAP) return data -def _build_daily_forecast_data(timestep: dict[str, Any]) -> Forecast: - data = Forecast(datetime=timestep["time"].isoformat()) +def _build_daily_forecast_data(timestep: dict[str, Any]) -> WeatherForecast: + data = WeatherForecast(datetime=timestep["time"].isoformat()) _populate_forecast_data(data, timestep, DAILY_FORECAST_ATTRIBUTE_MAP) return data -def _build_twice_daily_forecast_data(timestep: dict[str, Any]) -> Forecast: - data = Forecast(datetime=timestep["time"].isoformat()) +def _build_twice_daily_forecast_data(timestep: dict[str, Any]) -> WeatherForecast: + data = WeatherForecast(datetime=timestep["time"].isoformat()) # day and night forecasts have slightly different format if "daySignificantWeatherCode" in timestep: @@ -111,7 +111,7 @@ def _build_twice_daily_forecast_data(timestep: dict[str, Any]) -> Forecast: def _populate_forecast_data( - forecast: Forecast, timestep: dict[str, Any], mapping: dict[str, str] + forecast: WeatherForecast, timestep: dict[str, Any], mapping: dict[str, str] ) -> None: def get_mapped_attribute(attr: str) -> Any: if attr not in mapping: @@ -153,9 +153,9 @@ def get_mapped_attribute(attr: str) -> Any: class MetOfficeWeather( CoordinatorWeatherEntity[ - TimestampDataUpdateCoordinator[ForecastData], - TimestampDataUpdateCoordinator[ForecastData], - TimestampDataUpdateCoordinator[ForecastData], + TimestampDataUpdateCoordinator[Forecast], + TimestampDataUpdateCoordinator[Forecast], + TimestampDataUpdateCoordinator[Forecast], ] ): """Implementation of a Met Office weather condition.""" @@ -177,9 +177,9 @@ class MetOfficeWeather( def __init__( self, - coordinator_daily: TimestampDataUpdateCoordinator[ForecastData], - coordinator_hourly: TimestampDataUpdateCoordinator[ForecastData], - coordinator_twice_daily: TimestampDataUpdateCoordinator[ForecastData], + coordinator_daily: TimestampDataUpdateCoordinator[Forecast], + coordinator_hourly: TimestampDataUpdateCoordinator[Forecast], + coordinator_twice_daily: TimestampDataUpdateCoordinator[Forecast], hass_data: dict[str, Any], ) -> None: """Initialise the platform with a data instance.""" @@ -263,10 +263,10 @@ def wind_bearing(self) -> float | None: return float(value) if value is not None else None @callback - def _async_forecast_daily(self) -> list[Forecast] | None: + def _async_forecast_daily(self) -> list[WeatherForecast] | None: """Return the daily forecast in native units.""" coordinator = cast( - TimestampDataUpdateCoordinator[ForecastData], + TimestampDataUpdateCoordinator[Forecast], self.forecast_coordinators["daily"], ) timesteps = coordinator.data.timesteps @@ -277,10 +277,10 @@ def _async_forecast_daily(self) -> list[Forecast] | None: ] @callback - def _async_forecast_hourly(self) -> list[Forecast] | None: + def _async_forecast_hourly(self) -> list[WeatherForecast] | None: """Return the hourly forecast in native units.""" coordinator = cast( - TimestampDataUpdateCoordinator[ForecastData], + TimestampDataUpdateCoordinator[Forecast], self.forecast_coordinators["hourly"], ) @@ -292,10 +292,10 @@ def _async_forecast_hourly(self) -> list[Forecast] | None: ] @callback - def _async_forecast_twice_daily(self) -> list[Forecast] | None: + def _async_forecast_twice_daily(self) -> list[WeatherForecast] | None: """Return the twice daily forecast in native units.""" coordinator = cast( - TimestampDataUpdateCoordinator[ForecastData], + TimestampDataUpdateCoordinator[Forecast], self.forecast_coordinators["twice_daily"], ) timesteps = coordinator.data.timesteps diff --git a/homeassistant/components/onewire/strings.json b/homeassistant/components/onewire/strings.json index 6a13dfb87ddc3c..e1dfd8617f8bde 100644 --- a/homeassistant/components/onewire/strings.json +++ b/homeassistant/components/onewire/strings.json @@ -139,12 +139,12 @@ "step": { "device_selection": { "data": { - "clear_device_options": "Reset all device customisations", - "device_selection": "Customise specific devices" + "clear_device_options": "Reset all device customizations", + "device_selection": "Customize specific devices" }, "data_description": { "clear_device_options": "Use this to reset all device specific options to default values.", - "device_selection": "Customise behaviour of individual devices." + "device_selection": "Customize behavior of individual devices." }, "description": "Select what configuration steps to process", "title": "1-Wire device options" diff --git a/homeassistant/components/telegram_bot/bot.py b/homeassistant/components/telegram_bot/bot.py index a397b11daf054d..5f502c236bacd7 100644 --- a/homeassistant/components/telegram_bot/bot.py +++ b/homeassistant/components/telegram_bot/bot.py @@ -578,6 +578,7 @@ async def send_message( "Error sending message", params[ATTR_MESSAGE_TAG], text, + target=target, parse_mode=params[ATTR_PARSER], disable_web_page_preview=params[ATTR_DISABLE_WEB_PREV], disable_notification=params[ATTR_DISABLE_NOTIF], diff --git a/homeassistant/components/vesync/manifest.json b/homeassistant/components/vesync/manifest.json index 8749dd956ff01b..427b526d050418 100644 --- a/homeassistant/components/vesync/manifest.json +++ b/homeassistant/components/vesync/manifest.json @@ -13,5 +13,5 @@ "documentation": "https://www.home-assistant.io/integrations/vesync", "iot_class": "cloud_polling", "loggers": ["pyvesync"], - "requirements": ["pyvesync==3.1.0"] + "requirements": ["pyvesync==3.1.2"] } diff --git a/homeassistant/components/xbox/__init__.py b/homeassistant/components/xbox/__init__.py index 30bc7d59417b45..62e1c63129a772 100644 --- a/homeassistant/components/xbox/__init__.py +++ b/homeassistant/components/xbox/__init__.py @@ -8,14 +8,13 @@ from xbox.webapi.api.provider.smartglass.models import SmartglassConsoleList from xbox.webapi.common.signed_session import SignedSession -from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv from . import api from .const import DOMAIN -from .coordinator import XboxUpdateCoordinator +from .coordinator import XboxConfigEntry, XboxUpdateCoordinator _LOGGER = logging.getLogger(__name__) @@ -29,7 +28,7 @@ ] -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_setup_entry(hass: HomeAssistant, entry: XboxConfigEntry) -> bool: """Set up xbox from a config entry.""" implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( @@ -45,30 +44,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.debug( "Found %d consoles: %s", len(consoles.result), - consoles.dict(), + consoles.model_dump(), ) coordinator = XboxUpdateCoordinator(hass, entry, client, consoles) await coordinator.async_config_entry_first_refresh() - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { - "client": XboxLiveClient(auth), - "consoles": consoles, - "coordinator": coordinator, - } + entry.runtime_data = coordinator await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_unload_entry(hass: HomeAssistant, entry: XboxConfigEntry) -> bool: """Unload a config entry.""" - unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) - if unload_ok: - # Unsub from coordinator updates - hass.data[DOMAIN][entry.entry_id]["sensor_unsub"]() - hass.data[DOMAIN][entry.entry_id]["binary_sensor_unsub"]() - hass.data[DOMAIN].pop(entry.entry_id) - - return unload_ok + + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/xbox/binary_sensor.py b/homeassistant/components/xbox/binary_sensor.py index 5339c4d7a8efdc..b4177c773f1415 100644 --- a/homeassistant/components/xbox/binary_sensor.py +++ b/homeassistant/components/xbox/binary_sensor.py @@ -5,13 +5,11 @@ from functools import partial from homeassistant.components.binary_sensor import BinarySensorEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN -from .coordinator import XboxUpdateCoordinator +from .coordinator import XboxConfigEntry, XboxUpdateCoordinator from .entity import XboxBaseEntity PRESENCE_ATTRIBUTES = ["online", "in_party", "in_game", "in_multiplayer"] @@ -19,18 +17,16 @@ async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: XboxConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Xbox Live friends.""" - coordinator: XboxUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ - "coordinator" - ] + coordinator = entry.runtime_data update_friends = partial(async_update_friends, coordinator, {}, async_add_entities) - unsub = coordinator.async_add_listener(update_friends) - hass.data[DOMAIN][entry.entry_id]["binary_sensor_unsub"] = unsub + entry.async_on_unload(coordinator.async_add_listener(update_friends)) + update_friends() diff --git a/homeassistant/components/xbox/coordinator.py b/homeassistant/components/xbox/coordinator.py index 62c7a35e88b24c..bf5393d191771b 100644 --- a/homeassistant/components/xbox/coordinator.py +++ b/homeassistant/components/xbox/coordinator.py @@ -28,6 +28,8 @@ _LOGGER = logging.getLogger(__name__) +type XboxConfigEntry = ConfigEntry[XboxUpdateCoordinator] + @dataclass class ConsoleData: diff --git a/homeassistant/components/xbox/media_player.py b/homeassistant/components/xbox/media_player.py index 6464b2417cc9a3..505e5f51162ca8 100644 --- a/homeassistant/components/xbox/media_player.py +++ b/homeassistant/components/xbox/media_player.py @@ -5,13 +5,11 @@ import re from typing import Any -from xbox.webapi.api.client import XboxLiveClient from xbox.webapi.api.provider.catalog.models import Image from xbox.webapi.api.provider.smartglass.models import ( PlaybackState, PowerState, SmartglassConsole, - SmartglassConsoleList, VolumeDirection, ) @@ -21,7 +19,6 @@ MediaPlayerState, MediaType, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -29,7 +26,7 @@ from .browse_media import build_item_response from .const import DOMAIN -from .coordinator import ConsoleData, XboxUpdateCoordinator +from .coordinator import ConsoleData, XboxConfigEntry, XboxUpdateCoordinator SUPPORT_XBOX = ( MediaPlayerEntityFeature.TURN_ON @@ -57,18 +54,18 @@ async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: XboxConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Xbox media_player from a config entry.""" - client: XboxLiveClient = hass.data[DOMAIN][entry.entry_id]["client"] - consoles: SmartglassConsoleList = hass.data[DOMAIN][entry.entry_id]["consoles"] - coordinator: XboxUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ - "coordinator" - ] + + coordinator = entry.runtime_data async_add_entities( - [XboxMediaPlayer(client, console, coordinator) for console in consoles.result] + [ + XboxMediaPlayer(console, coordinator) + for console in coordinator.consoles.result + ] ) @@ -77,14 +74,13 @@ class XboxMediaPlayer(CoordinatorEntity[XboxUpdateCoordinator], MediaPlayerEntit def __init__( self, - client: XboxLiveClient, console: SmartglassConsole, coordinator: XboxUpdateCoordinator, ) -> None: """Initialize the Xbox Media Player.""" super().__init__(coordinator) - self.client: XboxLiveClient = client - self._console: SmartglassConsole = console + self.client = coordinator.client + self._console = console @property def name(self): diff --git a/homeassistant/components/xbox/media_source.py b/homeassistant/components/xbox/media_source.py index 4478502b4ca3a8..263aed3504ca5d 100644 --- a/homeassistant/components/xbox/media_source.py +++ b/homeassistant/components/xbox/media_source.py @@ -24,6 +24,7 @@ from .browse_media import _find_media_image from .const import DOMAIN +from .coordinator import XboxConfigEntry MIME_TYPE_MAP = { "gameclips": "video/mp4", @@ -38,8 +39,8 @@ async def async_get_media_source(hass: HomeAssistant): """Set up Xbox media source.""" - entry = hass.config_entries.async_entries(DOMAIN)[0] - client = hass.data[DOMAIN][entry.entry_id]["client"] + entry: XboxConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] + client = entry.runtime_data.client return XboxSource(hass, client) diff --git a/homeassistant/components/xbox/remote.py b/homeassistant/components/xbox/remote.py index 4e5893ddb13452..7f8545e0a20b72 100644 --- a/homeassistant/components/xbox/remote.py +++ b/homeassistant/components/xbox/remote.py @@ -7,12 +7,10 @@ import re from typing import Any -from xbox.webapi.api.client import XboxLiveClient from xbox.webapi.api.provider.smartglass.models import ( InputKeyType, PowerState, SmartglassConsole, - SmartglassConsoleList, ) from homeassistant.components.remote import ( @@ -21,30 +19,25 @@ DEFAULT_DELAY_SECS, RemoteEntity, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN -from .coordinator import ConsoleData, XboxUpdateCoordinator +from .coordinator import ConsoleData, XboxConfigEntry, XboxUpdateCoordinator async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: XboxConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Xbox media_player from a config entry.""" - client: XboxLiveClient = hass.data[DOMAIN][entry.entry_id]["client"] - consoles: SmartglassConsoleList = hass.data[DOMAIN][entry.entry_id]["consoles"] - coordinator: XboxUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ - "coordinator" - ] + coordinator = entry.runtime_data async_add_entities( - [XboxRemote(client, console, coordinator) for console in consoles.result] + [XboxRemote(console, coordinator) for console in coordinator.consoles.result] ) @@ -53,14 +46,13 @@ class XboxRemote(CoordinatorEntity[XboxUpdateCoordinator], RemoteEntity): def __init__( self, - client: XboxLiveClient, console: SmartglassConsole, coordinator: XboxUpdateCoordinator, ) -> None: """Initialize the Xbox Media Player.""" super().__init__(coordinator) - self.client: XboxLiveClient = client - self._console: SmartglassConsole = console + self.client = coordinator.client + self._console = console @property def name(self): diff --git a/homeassistant/components/xbox/sensor.py b/homeassistant/components/xbox/sensor.py index da53557a2d3eac..1082473738ce47 100644 --- a/homeassistant/components/xbox/sensor.py +++ b/homeassistant/components/xbox/sensor.py @@ -5,13 +5,11 @@ from functools import partial from homeassistant.components.sensor import SensorEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN -from .coordinator import XboxUpdateCoordinator +from .coordinator import XboxConfigEntry, XboxUpdateCoordinator from .entity import XboxBaseEntity SENSOR_ATTRIBUTES = ["status", "gamer_score", "account_tier", "gold_tenure"] @@ -19,18 +17,15 @@ async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: XboxConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Xbox Live friends.""" - coordinator: XboxUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id][ - "coordinator" - ] + coordinator = config_entry.runtime_data update_friends = partial(async_update_friends, coordinator, {}, async_add_entities) - unsub = coordinator.async_add_listener(update_friends) - hass.data[DOMAIN][config_entry.entry_id]["sensor_unsub"] = unsub + config_entry.async_on_unload(coordinator.async_add_listener(update_friends)) update_friends() diff --git a/pylint/plugins/hass_imports.py b/pylint/plugins/hass_imports.py index ae7644419dc691..08279efe7811cf 100644 --- a/pylint/plugins/hass_imports.py +++ b/pylint/plugins/hass_imports.py @@ -126,20 +126,13 @@ class ObsoleteImportMatch: } _IGNORE_ROOT_IMPORT = ( - "automation", "bluetooth", - "device_automation", "device_tracker", - "ffmpeg", - "ffmpeg_motion", - "google_assistant", "homeassistant", "homeassistant_hardware", "http", "recorder", "rest", - "script", - "stream", ) diff --git a/requirements_all.txt b/requirements_all.txt index f09771603b4457..89de005fb47ce4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -319,7 +319,7 @@ aiolookin==1.0.0 aiolyric==2.0.2 # homeassistant.components.mealie -aiomealie==1.0.0 +aiomealie==1.0.1 # homeassistant.components.modern_forms aiomodernforms==0.1.8 @@ -2629,7 +2629,7 @@ pyvera==0.3.16 pyversasense==0.0.6 # homeassistant.components.vesync -pyvesync==3.1.0 +pyvesync==3.1.2 # homeassistant.components.vizio pyvizio==0.1.61 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d8106b3c2e1743..7294352c9c3d45 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -301,7 +301,7 @@ aiolookin==1.0.0 aiolyric==2.0.2 # homeassistant.components.mealie -aiomealie==1.0.0 +aiomealie==1.0.1 # homeassistant.components.modern_forms aiomodernforms==0.1.8 @@ -2187,7 +2187,7 @@ pyuptimerobot==22.2.0 pyvera==0.3.16 # homeassistant.components.vesync -pyvesync==3.1.0 +pyvesync==3.1.2 # homeassistant.components.vizio pyvizio==0.1.61 diff --git a/tests/components/cloud/test_http_api.py b/tests/components/cloud/test_http_api.py index dd63c7165759a0..9d91619016dbe4 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -33,7 +33,9 @@ ) from homeassistant.components.cloud.const import DEFAULT_EXPOSED_DOMAINS, DOMAIN from homeassistant.components.cloud.http_api import validate_language_voice -from homeassistant.components.google_assistant.helpers import GoogleEntity +from homeassistant.components.google_assistant.helpers import ( # pylint: disable=hass-component-root-import + GoogleEntity, +) from homeassistant.components.homeassistant import exposed_entities from homeassistant.components.websocket_api import ERR_INVALID_FORMAT from homeassistant.core import HomeAssistant, State diff --git a/tests/components/knx/test_device_trigger.py b/tests/components/knx/test_device_trigger.py index 124ce60e47597c..5a252401bd328a 100644 --- a/tests/components/knx/test_device_trigger.py +++ b/tests/components/knx/test_device_trigger.py @@ -6,8 +6,8 @@ import voluptuous_serialize from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.knx import DOMAIN, device_trigger diff --git a/tests/components/lg_netcast/test_device_trigger.py b/tests/components/lg_netcast/test_device_trigger.py index c8d725afde1563..b5c970aff076c5 100644 --- a/tests/components/lg_netcast/test_device_trigger.py +++ b/tests/components/lg_netcast/test_device_trigger.py @@ -3,8 +3,8 @@ import pytest from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.lg_netcast import DOMAIN, device_trigger diff --git a/tests/components/matter/fixtures/nodes/door_lock.json b/tests/components/matter/fixtures/nodes/door_lock.json index acd327ac56c2d8..27907479f0748f 100644 --- a/tests/components/matter/fixtures/nodes/door_lock.json +++ b/tests/components/matter/fixtures/nodes/door_lock.json @@ -110,15 +110,6 @@ "0/46/65528": [], "0/46/65529": [], "0/46/65531": [0, 65528, 65529, 65530, 65531, 65532, 65533], - "0/47/0": 1, - "0/47/1": 0, - "0/47/2": "USB", - "0/47/6": 0, - "0/47/65532": 1, - "0/47/65533": 1, - "0/47/65528": [], - "0/47/65529": [], - "0/47/65531": [0, 1, 2, 6, 65528, 65529, 65530, 65531, 65532, 65533], "0/48/0": 0, "0/48/1": { "0": 60, diff --git a/tests/components/matter/fixtures/nodes/door_lock_with_unbolt.json b/tests/components/matter/fixtures/nodes/door_lock_with_unbolt.json index f1fb518452e8b0..fd26d5d1df3fee 100644 --- a/tests/components/matter/fixtures/nodes/door_lock_with_unbolt.json +++ b/tests/components/matter/fixtures/nodes/door_lock_with_unbolt.json @@ -110,15 +110,6 @@ "0/46/65528": [], "0/46/65529": [], "0/46/65531": [0, 65528, 65529, 65530, 65531, 65532, 65533], - "0/47/0": 1, - "0/47/1": 0, - "0/47/2": "USB", - "0/47/6": 0, - "0/47/65532": 1, - "0/47/65533": 1, - "0/47/65528": [], - "0/47/65529": [], - "0/47/65531": [0, 1, 2, 6, 65528, 65529, 65530, 65531, 65532, 65533], "0/48/0": 0, "0/48/1": { "0": 60, diff --git a/tests/components/nest/test_device_trigger.py b/tests/components/nest/test_device_trigger.py index 33f5c1637faf70..fe16c9ba85df4e 100644 --- a/tests/components/nest/test_device_trigger.py +++ b/tests/components/nest/test_device_trigger.py @@ -7,8 +7,8 @@ from pytest_unordered import unordered from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.nest import DOMAIN diff --git a/tests/components/samsungtv/test_device_trigger.py b/tests/components/samsungtv/test_device_trigger.py index adb80293744296..811ec1aa888597 100644 --- a/tests/components/samsungtv/test_device_trigger.py +++ b/tests/components/samsungtv/test_device_trigger.py @@ -3,8 +3,8 @@ import pytest from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.samsungtv import device_trigger diff --git a/tests/components/shelly/test_device_trigger.py b/tests/components/shelly/test_device_trigger.py index ca9edb19fa78d8..b23f56ef4a9796 100644 --- a/tests/components/shelly/test_device_trigger.py +++ b/tests/components/shelly/test_device_trigger.py @@ -7,8 +7,8 @@ from pytest_unordered import unordered from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.shelly.const import ( diff --git a/tests/components/telegram_bot/test_telegram_bot.py b/tests/components/telegram_bot/test_telegram_bot.py index 1410147633f62e..f306e5421145db 100644 --- a/tests/components/telegram_bot/test_telegram_bot.py +++ b/tests/components/telegram_bot/test_telegram_bot.py @@ -1445,3 +1445,31 @@ async def test_set_message_reaction( is_big=True, read_timeout=None, ) + + +async def test_send_message_multi_target( + hass: HomeAssistant, + mock_broadcast_config_entry: MockConfigEntry, + mock_external_calls: None, +) -> None: + """Test send message for entries with multiple chat_ids.""" + + mock_broadcast_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_broadcast_config_entry.entry_id) + await hass.async_block_till_done() + + # we have 2 whitelisted chat ids in the config subentries: 123456 and 654321 + # 123456 is the default target since it is the first in the list + # This test checks that the message is sent to the right target + + response = await hass.services.async_call( + DOMAIN, + SERVICE_SEND_MESSAGE, + {ATTR_TARGET: 654321, ATTR_MESSAGE: "test_message"}, + blocking=True, + return_response=True, + ) + + await hass.async_block_till_done() + assert response["chats"][0]["chat_id"] == 654321 + assert response["chats"][0]["message_id"] == 12345 diff --git a/tests/components/webostv/test_device_trigger.py b/tests/components/webostv/test_device_trigger.py index c14e8f4542a34d..d78f138d147a89 100644 --- a/tests/components/webostv/test_device_trigger.py +++ b/tests/components/webostv/test_device_trigger.py @@ -3,8 +3,8 @@ import pytest from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.webostv import DOMAIN, device_trigger diff --git a/tests/components/zha/test_device_trigger.py b/tests/components/zha/test_device_trigger.py index d0b6eec62bf38c..42e603d3e14d46 100644 --- a/tests/components/zha/test_device_trigger.py +++ b/tests/components/zha/test_device_trigger.py @@ -11,8 +11,8 @@ import zigpy.types from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.zha.helpers import get_zha_gateway diff --git a/tests/components/zwave_js/test_device_condition.py b/tests/components/zwave_js/test_device_condition.py index 123191e1f3ab95..8dfd123e03f061 100644 --- a/tests/components/zwave_js/test_device_condition.py +++ b/tests/components/zwave_js/test_device_condition.py @@ -11,8 +11,8 @@ from zwave_js_server.event import Event from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.zwave_js import DOMAIN, device_condition diff --git a/tests/components/zwave_js/test_device_trigger.py b/tests/components/zwave_js/test_device_trigger.py index 7ff76888ce4518..bd34a02e3a8776 100644 --- a/tests/components/zwave_js/test_device_trigger.py +++ b/tests/components/zwave_js/test_device_trigger.py @@ -10,8 +10,8 @@ from zwave_js_server.model.node import Node from homeassistant.components import automation -from homeassistant.components.device_automation import DeviceAutomationType -from homeassistant.components.device_automation.exceptions import ( +from homeassistant.components.device_automation import ( + DeviceAutomationType, InvalidDeviceAutomationConfig, ) from homeassistant.components.zwave_js import DOMAIN, device_trigger diff --git a/tests/helpers/test_llm.py b/tests/helpers/test_llm.py index 4fbb38e2aa0ad1..7011ba42b72043 100644 --- a/tests/helpers/test_llm.py +++ b/tests/helpers/test_llm.py @@ -10,7 +10,7 @@ from homeassistant.components import calendar, todo from homeassistant.components.homeassistant.exposed_entities import async_expose_entity from homeassistant.components.intent import async_register_timer_handler -from homeassistant.components.script.config import ScriptConfig +from homeassistant.components.script import ScriptConfig from homeassistant.core import Context, HomeAssistant, State, SupportsResponse from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import (