Merge branch 'dev' into fix-microsign-alt1

This commit is contained in:
Jan Bouwhuis
2025-06-01 20:14:12 +02:00
committed by GitHub
296 changed files with 2665 additions and 1676 deletions

View File

@@ -0,0 +1,6 @@
{
"domain": "shelly",
"name": "shelly",
"integrations": ["shelly"],
"iot_standards": ["zwave"]
}

View File

@@ -5,23 +5,22 @@ from __future__ import annotations
from datetime import timedelta from datetime import timedelta
import logging import logging
from airthings import Airthings, AirthingsDevice, AirthingsError from airthings import Airthings
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ID, Platform from homeassistant.const import CONF_ID, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_SECRET, DOMAIN from .const import CONF_SECRET
from .coordinator import AirthingsDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PLATFORMS: list[Platform] = [Platform.SENSOR] PLATFORMS: list[Platform] = [Platform.SENSOR]
SCAN_INTERVAL = timedelta(minutes=6) SCAN_INTERVAL = timedelta(minutes=6)
type AirthingsDataCoordinatorType = DataUpdateCoordinator[dict[str, AirthingsDevice]] type AirthingsConfigEntry = ConfigEntry[AirthingsDataUpdateCoordinator]
type AirthingsConfigEntry = ConfigEntry[AirthingsDataCoordinatorType]
async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) -> bool:
@@ -32,21 +31,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) ->
async_get_clientsession(hass), async_get_clientsession(hass),
) )
async def _update_method() -> dict[str, AirthingsDevice]: coordinator = AirthingsDataUpdateCoordinator(hass, airthings)
"""Get the latest data from Airthings."""
try:
return await airthings.update_devices() # type: ignore[no-any-return]
except AirthingsError as err:
raise UpdateFailed(f"Unable to fetch data: {err}") from err
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
config_entry=entry,
name=DOMAIN,
update_method=_update_method,
update_interval=SCAN_INTERVAL,
)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator

View File

@@ -0,0 +1,36 @@
"""The Airthings integration."""
from datetime import timedelta
import logging
from airthings import Airthings, AirthingsDevice, AirthingsError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(minutes=6)
class AirthingsDataUpdateCoordinator(DataUpdateCoordinator[dict[str, AirthingsDevice]]):
"""Coordinator for Airthings data updates."""
def __init__(self, hass: HomeAssistant, airthings: Airthings) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_method=self._update_method,
update_interval=SCAN_INTERVAL,
)
self.airthings = airthings
async def _update_method(self) -> dict[str, AirthingsDevice]:
"""Get the latest data from Airthings."""
try:
return await self.airthings.update_devices() # type: ignore[no-any-return]
except AirthingsError as err:
raise UpdateFailed(f"Unable to fetch data: {err}") from err

View File

@@ -19,6 +19,7 @@ from homeassistant.const import (
SIGNAL_STRENGTH_DECIBELS, SIGNAL_STRENGTH_DECIBELS,
EntityCategory, EntityCategory,
UnitOfPressure, UnitOfPressure,
UnitOfSoundPressure,
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -27,8 +28,9 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AirthingsConfigEntry, AirthingsDataCoordinatorType from . import AirthingsConfigEntry
from .const import DOMAIN from .const import DOMAIN
from .coordinator import AirthingsDataUpdateCoordinator
SENSORS: dict[str, SensorEntityDescription] = { SENSORS: dict[str, SensorEntityDescription] = {
"radonShortTermAvg": SensorEntityDescription( "radonShortTermAvg": SensorEntityDescription(
@@ -54,6 +56,12 @@ SENSORS: dict[str, SensorEntityDescription] = {
native_unit_of_measurement=UnitOfPressure.MBAR, native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
"sla": SensorEntityDescription(
key="sla",
device_class=SensorDeviceClass.SOUND_PRESSURE,
native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A,
state_class=SensorStateClass.MEASUREMENT,
),
"battery": SensorEntityDescription( "battery": SensorEntityDescription(
key="battery", key="battery",
device_class=SensorDeviceClass.BATTERY, device_class=SensorDeviceClass.BATTERY,
@@ -140,7 +148,7 @@ async def async_setup_entry(
class AirthingsHeaterEnergySensor( class AirthingsHeaterEnergySensor(
CoordinatorEntity[AirthingsDataCoordinatorType], SensorEntity CoordinatorEntity[AirthingsDataUpdateCoordinator], SensorEntity
): ):
"""Representation of a Airthings Sensor device.""" """Representation of a Airthings Sensor device."""
@@ -149,7 +157,7 @@ class AirthingsHeaterEnergySensor(
def __init__( def __init__(
self, self,
coordinator: AirthingsDataCoordinatorType, coordinator: AirthingsDataUpdateCoordinator,
airthings_device: AirthingsDevice, airthings_device: AirthingsDevice,
entity_description: SensorEntityDescription, entity_description: SensorEntityDescription,
) -> None: ) -> None:

View File

@@ -4,27 +4,114 @@
"codeowners": ["@chemelli74"], "codeowners": ["@chemelli74"],
"config_flow": true, "config_flow": true,
"dhcp": [ "dhcp": [
{ "macaddress": "007147*" },
{ "macaddress": "00FC8B*" },
{ "macaddress": "0812A5*" },
{ "macaddress": "086AE5*" },
{ "macaddress": "08849D*" },
{ "macaddress": "089115*" },
{ "macaddress": "08A6BC*" }, { "macaddress": "08A6BC*" },
{ "macaddress": "08C224*" },
{ "macaddress": "0CDC91*" },
{ "macaddress": "0CEE99*" },
{ "macaddress": "1009F9*" },
{ "macaddress": "109693*" },
{ "macaddress": "10BF67*" }, { "macaddress": "10BF67*" },
{ "macaddress": "10CE02*" },
{ "macaddress": "140AC5*" },
{ "macaddress": "149138*" },
{ "macaddress": "1848BE*" },
{ "macaddress": "1C12B0*" },
{ "macaddress": "1C4D66*" },
{ "macaddress": "1C93C4*" },
{ "macaddress": "1CFE2B*" },
{ "macaddress": "244CE3*" },
{ "macaddress": "24CE33*" },
{ "macaddress": "2873F6*" },
{ "macaddress": "2C71FF*" },
{ "macaddress": "34AFB3*" },
{ "macaddress": "34D270*" },
{ "macaddress": "38F73D*" },
{ "macaddress": "3C5CC4*" },
{ "macaddress": "3CE441*" },
{ "macaddress": "440049*" }, { "macaddress": "440049*" },
{ "macaddress": "40A2DB*" },
{ "macaddress": "40A9CF*" },
{ "macaddress": "40B4CD*" },
{ "macaddress": "443D54*" }, { "macaddress": "443D54*" },
{ "macaddress": "44650D*" },
{ "macaddress": "485F2D*" },
{ "macaddress": "48785E*" },
{ "macaddress": "48B423*" }, { "macaddress": "48B423*" },
{ "macaddress": "4C1744*" }, { "macaddress": "4C1744*" },
{ "macaddress": "4CEFC0*" },
{ "macaddress": "5007C3*" },
{ "macaddress": "50D45C*" }, { "macaddress": "50D45C*" },
{ "macaddress": "50DCE7*" }, { "macaddress": "50DCE7*" },
{ "macaddress": "50F5DA*" },
{ "macaddress": "5C415A*" },
{ "macaddress": "6837E9*" },
{ "macaddress": "6854FD*" },
{ "macaddress": "689A87*" },
{ "macaddress": "68B691*" },
{ "macaddress": "68DBF5*" },
{ "macaddress": "68F63B*" }, { "macaddress": "68F63B*" },
{ "macaddress": "6C0C9A*" }, { "macaddress": "6C0C9A*" },
{ "macaddress": "6C5697*" },
{ "macaddress": "7458F3*" },
{ "macaddress": "74C246*" },
{ "macaddress": "74D637*" }, { "macaddress": "74D637*" },
{ "macaddress": "74E20C*" },
{ "macaddress": "74ECB2*" },
{ "macaddress": "786C84*" },
{ "macaddress": "78A03F*" },
{ "macaddress": "7C6166*" }, { "macaddress": "7C6166*" },
{ "macaddress": "7C6305*" },
{ "macaddress": "7CD566*" },
{ "macaddress": "8871E5*" },
{ "macaddress": "901195*" }, { "macaddress": "901195*" },
{ "macaddress": "90235B*" },
{ "macaddress": "90A822*" },
{ "macaddress": "90F82E*" },
{ "macaddress": "943A91*" }, { "macaddress": "943A91*" },
{ "macaddress": "98226E*" }, { "macaddress": "98226E*" },
{ "macaddress": "98CCF3*" },
{ "macaddress": "9CC8E9*" }, { "macaddress": "9CC8E9*" },
{ "macaddress": "A002DC*" },
{ "macaddress": "A0D2B1*" },
{ "macaddress": "A40801*" },
{ "macaddress": "A8E621*" }, { "macaddress": "A8E621*" },
{ "macaddress": "AC416A*" },
{ "macaddress": "AC63BE*" },
{ "macaddress": "ACCCFC*" },
{ "macaddress": "B0739C*" },
{ "macaddress": "B0CFCB*" },
{ "macaddress": "B0F7C4*" },
{ "macaddress": "B85F98*" },
{ "macaddress": "C091B9*" },
{ "macaddress": "C095CF*" }, { "macaddress": "C095CF*" },
{ "macaddress": "C49500*" },
{ "macaddress": "C86C3D*" },
{ "macaddress": "CC9EA2*" },
{ "macaddress": "CCF735*" },
{ "macaddress": "DC54D7*" },
{ "macaddress": "D8BE65*" }, { "macaddress": "D8BE65*" },
{ "macaddress": "D8FBD6*" },
{ "macaddress": "DC91BF*" },
{ "macaddress": "DCA0D0*" },
{ "macaddress": "E0F728*" },
{ "macaddress": "EC2BEB*" }, { "macaddress": "EC2BEB*" },
{ "macaddress": "F02F9E*" } { "macaddress": "EC8AC4*" },
{ "macaddress": "ECA138*" },
{ "macaddress": "F02F9E*" },
{ "macaddress": "F0272D*" },
{ "macaddress": "F0F0A4*" },
{ "macaddress": "F4032A*" },
{ "macaddress": "F854B8*" },
{ "macaddress": "FC492D*" },
{ "macaddress": "FC65DE*" },
{ "macaddress": "FCA183*" },
{ "macaddress": "FCE9D8*" }
], ],
"documentation": "https://www.home-assistant.io/integrations/amazon_devices", "documentation": "https://www.home-assistant.io/integrations/amazon_devices",
"integration_type": "hub", "integration_type": "hub",

View File

@@ -7,5 +7,5 @@
"integration_type": "device", "integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["pyaprilaire"], "loggers": ["pyaprilaire"],
"requirements": ["pyaprilaire==0.9.0"] "requirements": ["pyaprilaire==0.9.1"]
} }

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation", "documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system", "integration_type": "system",
"quality_scale": "internal", "quality_scale": "internal",
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.5.7"] "requirements": ["hassil==2.2.3", "home-assistant-intents==2025.5.28"]
} }

View File

@@ -1 +1,3 @@
"""The decora component.""" """The decora component."""
DOMAIN = "decora"

View File

@@ -21,7 +21,11 @@ from homeassistant.components.light import (
LightEntity, LightEntity,
) )
from homeassistant.const import CONF_API_KEY, CONF_DEVICES, CONF_NAME from homeassistant.const import CONF_API_KEY, CONF_DEVICES, CONF_NAME
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from . import DOMAIN
if TYPE_CHECKING: if TYPE_CHECKING:
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -90,6 +94,21 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up an Decora switch.""" """Set up an Decora switch."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Leviton Decora",
},
)
lights = [] lights = []
for address, device_config in config[CONF_DEVICES].items(): for address, device_config in config[CONF_DEVICES].items():
device = {} device = {}

View File

@@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend", "documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system", "integration_type": "system",
"quality_scale": "internal", "quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250527.0"] "requirements": ["home-assistant-frontend==20250531.0"]
} }

View File

@@ -21,6 +21,6 @@
"documentation": "https://www.home-assistant.io/integrations/home_connect", "documentation": "https://www.home-assistant.io/integrations/home_connect",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["aiohomeconnect"], "loggers": ["aiohomeconnect"],
"requirements": ["aiohomeconnect==0.17.0"], "requirements": ["aiohomeconnect==0.17.1"],
"zeroconf": ["_homeconnect._tcp.local."] "zeroconf": ["_homeconnect._tcp.local."]
} }

View File

@@ -18,6 +18,10 @@
"title": "The {integration_title} YAML configuration is being removed", "title": "The {integration_title} YAML configuration is being removed",
"description": "Configuring {integration_title} using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the `{domain}` configuration from your configuration.yaml file and restart Home Assistant to fix this issue." "description": "Configuring {integration_title} using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the `{domain}` configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
}, },
"deprecated_system_packages_config_flow_integration": {
"title": "The {integration_title} integration is being removed",
"description": "The {integration_title} integration is being removed as it requires additional system packages, which can't be installed on supported Home Assistant installations. Remove all \"{integration_title}\" config entries to fix this issue."
},
"deprecated_system_packages_yaml_integration": { "deprecated_system_packages_yaml_integration": {
"title": "The {integration_title} integration is being removed", "title": "The {integration_title} integration is being removed",
"description": "The {integration_title} integration is being removed as it requires additional system packages, which can't be installed on supported Home Assistant installations. Remove the `{domain}` configuration from your configuration.yaml file and restart Home Assistant to fix this issue." "description": "The {integration_title} integration is being removed as it requires additional system packages, which can't be installed on supported Home Assistant installations. Remove the `{domain}` configuration from your configuration.yaml file and restart Home Assistant to fix this issue."

View File

@@ -8,5 +8,5 @@
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["aioimmich"], "loggers": ["aioimmich"],
"quality_scale": "silver", "quality_scale": "silver",
"requirements": ["aioimmich==0.6.0"] "requirements": ["aioimmich==0.7.0"]
} }

View File

@@ -3,7 +3,6 @@
from __future__ import annotations from __future__ import annotations
from logging import getLogger from logging import getLogger
import mimetypes
from aiohttp.web import HTTPNotFound, Request, Response, StreamResponse from aiohttp.web import HTTPNotFound, Request, Response, StreamResponse
from aioimmich.exceptions import ImmichError from aioimmich.exceptions import ImmichError
@@ -30,11 +29,8 @@ LOGGER = getLogger(__name__)
async def async_get_media_source(hass: HomeAssistant) -> MediaSource: async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
"""Set up Immich media source.""" """Set up Immich media source."""
entries = hass.config_entries.async_entries(
DOMAIN, include_disabled=False, include_ignore=False
)
hass.http.register_view(ImmichMediaView(hass)) hass.http.register_view(ImmichMediaView(hass))
return ImmichMediaSource(hass, entries) return ImmichMediaSource(hass)
class ImmichMediaSourceIdentifier: class ImmichMediaSourceIdentifier:
@@ -42,12 +38,14 @@ class ImmichMediaSourceIdentifier:
def __init__(self, identifier: str) -> None: def __init__(self, identifier: str) -> None:
"""Split identifier into parts.""" """Split identifier into parts."""
parts = identifier.split("/") parts = identifier.split("|")
# coonfig_entry.unique_id/album_id/asset_it/filename # config_entry.unique_id|collection|collection_id|asset_id|file_name|mime_type
self.unique_id = parts[0] self.unique_id = parts[0]
self.album_id = parts[1] if len(parts) > 1 else None self.collection = parts[1] if len(parts) > 1 else None
self.asset_id = parts[2] if len(parts) > 2 else None self.collection_id = parts[2] if len(parts) > 2 else None
self.file_name = parts[3] if len(parts) > 2 else None self.asset_id = parts[3] if len(parts) > 3 else None
self.file_name = parts[4] if len(parts) > 3 else None
self.mime_type = parts[5] if len(parts) > 3 else None
class ImmichMediaSource(MediaSource): class ImmichMediaSource(MediaSource):
@@ -55,18 +53,17 @@ class ImmichMediaSource(MediaSource):
name = "Immich" name = "Immich"
def __init__(self, hass: HomeAssistant, entries: list[ConfigEntry]) -> None: def __init__(self, hass: HomeAssistant) -> None:
"""Initialize Immich media source.""" """Initialize Immich media source."""
super().__init__(DOMAIN) super().__init__(DOMAIN)
self.hass = hass self.hass = hass
self.entries = entries
async def async_browse_media( async def async_browse_media(
self, self,
item: MediaSourceItem, item: MediaSourceItem,
) -> BrowseMediaSource: ) -> BrowseMediaSource:
"""Return media.""" """Return media."""
if not self.hass.config_entries.async_loaded_entries(DOMAIN): if not (entries := self.hass.config_entries.async_loaded_entries(DOMAIN)):
raise BrowseError("Immich is not configured") raise BrowseError("Immich is not configured")
return BrowseMediaSource( return BrowseMediaSource(
domain=DOMAIN, domain=DOMAIN,
@@ -78,15 +75,16 @@ class ImmichMediaSource(MediaSource):
can_expand=True, can_expand=True,
children_media_class=MediaClass.DIRECTORY, children_media_class=MediaClass.DIRECTORY,
children=[ children=[
*await self._async_build_immich(item), *await self._async_build_immich(item, entries),
], ],
) )
async def _async_build_immich( async def _async_build_immich(
self, item: MediaSourceItem self, item: MediaSourceItem, entries: list[ConfigEntry]
) -> list[BrowseMediaSource]: ) -> list[BrowseMediaSource]:
"""Handle browsing different immich instances.""" """Handle browsing different immich instances."""
if not item.identifier: if not item.identifier:
LOGGER.debug("Render all Immich instances")
return [ return [
BrowseMediaSource( BrowseMediaSource(
domain=DOMAIN, domain=DOMAIN,
@@ -97,7 +95,7 @@ class ImmichMediaSource(MediaSource):
can_play=False, can_play=False,
can_expand=True, can_expand=True,
) )
for entry in self.entries for entry in entries
] ]
identifier = ImmichMediaSourceIdentifier(item.identifier) identifier = ImmichMediaSourceIdentifier(item.identifier)
entry: ImmichConfigEntry | None = ( entry: ImmichConfigEntry | None = (
@@ -108,8 +106,22 @@ class ImmichMediaSource(MediaSource):
assert entry assert entry
immich_api = entry.runtime_data.api immich_api = entry.runtime_data.api
if identifier.album_id is None: if identifier.collection is None:
# Get Albums LOGGER.debug("Render all collections for %s", entry.title)
return [
BrowseMediaSource(
domain=DOMAIN,
identifier=f"{identifier.unique_id}|albums",
media_class=MediaClass.DIRECTORY,
media_content_type=MediaClass.IMAGE,
title="albums",
can_play=False,
can_expand=True,
)
]
if identifier.collection_id is None:
LOGGER.debug("Render all albums for %s", entry.title)
try: try:
albums = await immich_api.albums.async_get_all_albums() albums = await immich_api.albums.async_get_all_albums()
except ImmichError: except ImmichError:
@@ -118,21 +130,25 @@ class ImmichMediaSource(MediaSource):
return [ return [
BrowseMediaSource( BrowseMediaSource(
domain=DOMAIN, domain=DOMAIN,
identifier=f"{item.identifier}/{album.album_id}", identifier=f"{identifier.unique_id}|albums|{album.album_id}",
media_class=MediaClass.DIRECTORY, media_class=MediaClass.DIRECTORY,
media_content_type=MediaClass.IMAGE, media_content_type=MediaClass.IMAGE,
title=album.name, title=album.name,
can_play=False, can_play=False,
can_expand=True, can_expand=True,
thumbnail=f"/immich/{identifier.unique_id}/{album.thumbnail_asset_id}/thumb.jpg/thumbnail", thumbnail=f"/immich/{identifier.unique_id}/{album.thumbnail_asset_id}/thumbnail/image/jpg",
) )
for album in albums for album in albums
] ]
# Request items of album LOGGER.debug(
"Render all assets of album %s for %s",
identifier.collection_id,
entry.title,
)
try: try:
album_info = await immich_api.albums.async_get_album_info( album_info = await immich_api.albums.async_get_album_info(
identifier.album_id identifier.collection_id
) )
except ImmichError: except ImmichError:
return [] return []
@@ -141,17 +157,18 @@ class ImmichMediaSource(MediaSource):
BrowseMediaSource( BrowseMediaSource(
domain=DOMAIN, domain=DOMAIN,
identifier=( identifier=(
f"{identifier.unique_id}/" f"{identifier.unique_id}|albums|"
f"{identifier.album_id}/" f"{identifier.collection_id}|"
f"{asset.asset_id}/" f"{asset.asset_id}|"
f"{asset.file_name}" f"{asset.file_name}|"
f"{asset.mime_type}"
), ),
media_class=MediaClass.IMAGE, media_class=MediaClass.IMAGE,
media_content_type=asset.mime_type, media_content_type=asset.mime_type,
title=asset.file_name, title=asset.file_name,
can_play=False, can_play=False,
can_expand=False, can_expand=False,
thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/{asset.file_name}/thumbnail", thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/thumbnail/{asset.mime_type}",
) )
for asset in album_info.assets for asset in album_info.assets
if asset.mime_type.startswith("image/") if asset.mime_type.startswith("image/")
@@ -161,17 +178,18 @@ class ImmichMediaSource(MediaSource):
BrowseMediaSource( BrowseMediaSource(
domain=DOMAIN, domain=DOMAIN,
identifier=( identifier=(
f"{identifier.unique_id}/" f"{identifier.unique_id}|albums|"
f"{identifier.album_id}/" f"{identifier.collection_id}|"
f"{asset.asset_id}/" f"{asset.asset_id}|"
f"{asset.file_name}" f"{asset.file_name}|"
f"{asset.mime_type}"
), ),
media_class=MediaClass.VIDEO, media_class=MediaClass.VIDEO,
media_content_type=asset.mime_type, media_content_type=asset.mime_type,
title=asset.file_name, title=asset.file_name,
can_play=True, can_play=True,
can_expand=False, can_expand=False,
thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/thumbnail.jpg/thumbnail", thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/thumbnail/image/jpeg",
) )
for asset in album_info.assets for asset in album_info.assets
if asset.mime_type.startswith("video/") if asset.mime_type.startswith("video/")
@@ -181,17 +199,23 @@ class ImmichMediaSource(MediaSource):
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia: async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
"""Resolve media to a url.""" """Resolve media to a url."""
identifier = ImmichMediaSourceIdentifier(item.identifier) try:
if identifier.file_name is None: identifier = ImmichMediaSourceIdentifier(item.identifier)
raise Unresolvable("No file name") except IndexError as err:
mime_type, _ = mimetypes.guess_type(identifier.file_name) raise Unresolvable(
if not isinstance(mime_type, str): f"Could not parse identifier: {item.identifier}"
raise Unresolvable("No file extension") ) from err
if identifier.mime_type is None:
raise Unresolvable(
f"Could not resolve identifier that has no mime-type: {item.identifier}"
)
return PlayMedia( return PlayMedia(
( (
f"/immich/{identifier.unique_id}/{identifier.asset_id}/{identifier.file_name}/fullsize" f"/immich/{identifier.unique_id}/{identifier.asset_id}/fullsize/{identifier.mime_type}"
), ),
mime_type, identifier.mime_type,
) )
@@ -212,10 +236,10 @@ class ImmichMediaView(HomeAssistantView):
if not self.hass.config_entries.async_loaded_entries(DOMAIN): if not self.hass.config_entries.async_loaded_entries(DOMAIN):
raise HTTPNotFound raise HTTPNotFound
asset_id, file_name, size = location.split("/") try:
mime_type, _ = mimetypes.guess_type(file_name) asset_id, size, mime_type_base, mime_type_format = location.split("/")
if not isinstance(mime_type, str): except ValueError as err:
raise HTTPNotFound raise HTTPNotFound from err
entry: ImmichConfigEntry | None = ( entry: ImmichConfigEntry | None = (
self.hass.config_entries.async_entry_for_domain_unique_id( self.hass.config_entries.async_entry_for_domain_unique_id(
@@ -226,7 +250,7 @@ class ImmichMediaView(HomeAssistantView):
immich_api = entry.runtime_data.api immich_api = entry.runtime_data.api
# stream response for videos # stream response for videos
if mime_type.startswith("video/"): if mime_type_base == "video":
try: try:
resp = await immich_api.assets.async_play_video_stream(asset_id) resp = await immich_api.assets.async_play_video_stream(asset_id)
except ImmichError as exc: except ImmichError as exc:
@@ -243,4 +267,4 @@ class ImmichMediaView(HomeAssistantView):
image = await immich_api.assets.async_view_asset(asset_id, size) image = await immich_api.assets.async_view_asset(asset_id, size)
except ImmichError as exc: except ImmichError as exc:
raise HTTPNotFound from exc raise HTTPNotFound from exc
return Response(body=image, content_type=mime_type) return Response(body=image, content_type=f"{mime_type_base}/{mime_type_format}")

View File

@@ -7,5 +7,5 @@
"integration_type": "hub", "integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["pyiskra"], "loggers": ["pyiskra"],
"requirements": ["pyiskra==0.1.15"] "requirements": ["pyiskra==0.1.19"]
} }

View File

@@ -7,7 +7,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from .client_wrapper import CannotConnect, InvalidAuth, create_client, validate_input from .client_wrapper import CannotConnect, InvalidAuth, create_client, validate_input
from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, PLATFORMS from .const import CONF_CLIENT_DEVICE_ID, DEFAULT_NAME, DOMAIN, PLATFORMS
from .coordinator import JellyfinConfigEntry, JellyfinDataUpdateCoordinator from .coordinator import JellyfinConfigEntry, JellyfinDataUpdateCoordinator
@@ -35,9 +35,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: JellyfinConfigEntry) ->
coordinator = JellyfinDataUpdateCoordinator( coordinator = JellyfinDataUpdateCoordinator(
hass, entry, client, server_info, user_id hass, entry, client, server_info, user_id
) )
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
entry_type=dr.DeviceEntryType.SERVICE,
identifiers={(DOMAIN, coordinator.server_id)},
manufacturer=DEFAULT_NAME,
name=coordinator.server_name,
sw_version=coordinator.server_version,
)
entry.runtime_data = coordinator entry.runtime_data = coordinator
entry.async_on_unload(client.stop) entry.async_on_unload(client.stop)

View File

@@ -4,10 +4,10 @@ from __future__ import annotations
from typing import Any from typing import Any
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DEFAULT_NAME, DOMAIN from .const import DOMAIN
from .coordinator import JellyfinDataUpdateCoordinator from .coordinator import JellyfinDataUpdateCoordinator
@@ -24,11 +24,7 @@ class JellyfinServerEntity(JellyfinEntity):
"""Initialize the Jellyfin entity.""" """Initialize the Jellyfin entity."""
super().__init__(coordinator) super().__init__(coordinator)
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, coordinator.server_id)}, identifiers={(DOMAIN, coordinator.server_id)},
manufacturer=DEFAULT_NAME,
name=coordinator.server_name,
sw_version=coordinator.server_version,
) )

View File

@@ -11,8 +11,9 @@ from homeassistant.const import (
SERVICE_VOLUME_MUTE, SERVICE_VOLUME_MUTE,
SERVICE_VOLUME_UP, SERVICE_VOLUME_UP,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
DOMAIN = "keyboard" DOMAIN = "keyboard"
@@ -24,6 +25,20 @@ CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
def setup(hass: HomeAssistant, config: ConfigType) -> bool: def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Listen for keyboard events.""" """Listen for keyboard events."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Keyboard",
},
)
keyboard = PyKeyboard() keyboard = PyKeyboard()
keyboard.special_key_assignment() keyboard.special_key_assignment()

View File

@@ -20,8 +20,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import DOMAIN from .const import DOMAIN
SCAN_INTERVAL = timedelta(seconds=15) SCAN_INTERVAL = timedelta(seconds=15)
SETTINGS_UPDATE_INTERVAL = timedelta(hours=1) SETTINGS_UPDATE_INTERVAL = timedelta(hours=8)
SCHEDULE_UPDATE_INTERVAL = timedelta(minutes=5) SCHEDULE_UPDATE_INTERVAL = timedelta(minutes=30)
STATISTICS_UPDATE_INTERVAL = timedelta(minutes=15) STATISTICS_UPDATE_INTERVAL = timedelta(minutes=15)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@@ -37,5 +37,5 @@
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["pylamarzocco"], "loggers": ["pylamarzocco"],
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["pylamarzocco==2.0.7"] "requirements": ["pylamarzocco==2.0.8"]
} }

View File

@@ -7,6 +7,6 @@
"integration_type": "hub", "integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["linkplay"], "loggers": ["linkplay"],
"requirements": ["python-linkplay==0.2.8"], "requirements": ["python-linkplay==0.2.9"],
"zeroconf": ["_linkplay._tcp.local."] "zeroconf": ["_linkplay._tcp.local."]
} }

View File

@@ -7,8 +7,9 @@ import time
import lirc import lirc
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -26,6 +27,20 @@ CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
def setup(hass: HomeAssistant, config: ConfigType) -> bool: def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the LIRC capability.""" """Set up the LIRC capability."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "LIRC",
},
)
# blocking=True gives unexpected behavior (multiple responses for 1 press) # blocking=True gives unexpected behavior (multiple responses for 1 press)
# also by not blocking, we allow hass to shut down the thread gracefully # also by not blocking, we allow hass to shut down the thread gracefully
# on exit. # on exit.

View File

@@ -40,7 +40,7 @@ SUPPORT_FLAGS = (
PRESET_MODES = [PRESET_NONE, PRESET_COMFORT, PRESET_ECO, PRESET_AWAY] PRESET_MODES = [PRESET_NONE, PRESET_COMFORT, PRESET_ECO, PRESET_AWAY]
MIN_TEMPERATURE = 7 MIN_TEMPERATURE = 7
MAX_TEMPERATURE = 40 MAX_TEMPERATURE = 30
async def async_setup_entry( async def async_setup_entry(

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/opower", "documentation": "https://www.home-assistant.io/integrations/opower",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["opower"], "loggers": ["opower"],
"requirements": ["opower==0.12.2"] "requirements": ["opower==0.12.3"]
} }

View File

@@ -15,5 +15,5 @@
"integration_type": "device", "integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "bronze", "quality_scale": "bronze",
"requirements": ["pyprobeplus==1.0.0"] "requirements": ["pyprobeplus==1.0.1"]
} }

View File

@@ -7,7 +7,7 @@
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "internal", "quality_scale": "internal",
"requirements": [ "requirements": [
"SQLAlchemy==2.0.40", "SQLAlchemy==2.0.41",
"fnv-hash-fast==1.5.0", "fnv-hash-fast==1.5.0",
"psutil-home-assistant==0.0.1" "psutil-home-assistant==0.0.1"
] ]

View File

@@ -233,6 +233,14 @@ async def async_setup_entry(
"privacy_mode_change", async_privacy_mode_change, 623 "privacy_mode_change", async_privacy_mode_change, 623
) )
# ensure host device is setup before connected camera devices that use via_device
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
identifiers={(DOMAIN, host.unique_id)},
connections={(dr.CONNECTION_NETWORK_MAC, host.api.mac_address)},
)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
config_entry.async_on_unload( config_entry.async_on_unload(

View File

@@ -462,6 +462,12 @@
"doorbell_button_sound": { "doorbell_button_sound": {
"default": "mdi:volume-high" "default": "mdi:volume-high"
}, },
"hardwired_chime_enabled": {
"default": "mdi:bell",
"state": {
"off": "mdi:bell-off"
}
},
"hdr": { "hdr": {
"default": "mdi:hdr" "default": "mdi:hdr"
}, },

View File

@@ -19,5 +19,5 @@
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["reolink_aio"], "loggers": ["reolink_aio"],
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["reolink-aio==0.13.3"] "requirements": ["reolink-aio==0.13.4"]
} }

View File

@@ -910,6 +910,9 @@
"auto_focus": { "auto_focus": {
"name": "Auto focus" "name": "Auto focus"
}, },
"hardwired_chime_enabled": {
"name": "Hardwired chime enabled"
},
"guard_return": { "guard_return": {
"name": "Guard return" "name": "Guard return"
}, },

View File

@@ -216,6 +216,16 @@ SWITCH_ENTITIES = (
value=lambda api, ch: api.baichuan.privacy_mode(ch), value=lambda api, ch: api.baichuan.privacy_mode(ch),
method=lambda api, ch, value: api.baichuan.set_privacy_mode(ch, value), method=lambda api, ch, value: api.baichuan.set_privacy_mode(ch, value),
), ),
ReolinkSwitchEntityDescription(
key="hardwired_chime_enabled",
cmd_key="483",
translation_key="hardwired_chime_enabled",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
supported=lambda api, ch: api.supported(ch, "hardwired_chime"),
value=lambda api, ch: api.baichuan.hardwired_chime_enabled(ch),
method=lambda api, ch, value: api.baichuan.set_ding_dong_ctrl(ch, enable=value),
),
) )
NVR_SWITCH_ENTITIES = ( NVR_SWITCH_ENTITIES = (

View File

@@ -52,6 +52,7 @@ class PlaybackProxyView(HomeAssistantView):
verify_ssl=False, verify_ssl=False,
ssl_cipher=SSLCipherList.INSECURE, ssl_cipher=SSLCipherList.INSECURE,
) )
self._vod_type: str | None = None
async def get( async def get(
self, self,
@@ -68,6 +69,8 @@ class PlaybackProxyView(HomeAssistantView):
filename_decoded = urlsafe_b64decode(filename.encode("utf-8")).decode("utf-8") filename_decoded = urlsafe_b64decode(filename.encode("utf-8")).decode("utf-8")
ch = int(channel) ch = int(channel)
if self._vod_type is not None:
vod_type = self._vod_type
try: try:
host = get_host(self.hass, config_entry_id) host = get_host(self.hass, config_entry_id)
except Unresolvable: except Unresolvable:
@@ -127,6 +130,25 @@ class PlaybackProxyView(HomeAssistantView):
"apolication/octet-stream", "apolication/octet-stream",
]: ]:
err_str = f"Reolink playback expected video/mp4 but got {reolink_response.content_type}" err_str = f"Reolink playback expected video/mp4 but got {reolink_response.content_type}"
if (
reolink_response.content_type == "video/x-flv"
and vod_type == VodRequestType.PLAYBACK.value
):
# next time use DOWNLOAD immediately
self._vod_type = VodRequestType.DOWNLOAD.value
_LOGGER.debug(
"%s, retrying using download instead of playback cmd", err_str
)
return await self.get(
request,
config_entry_id,
channel,
stream_res,
self._vod_type,
filename,
retry,
)
_LOGGER.error(err_str) _LOGGER.error(err_str)
if reolink_response.content_type == "text/html": if reolink_response.content_type == "text/html":
text = await reolink_response.text() text = await reolink_response.text()
@@ -140,7 +162,10 @@ class PlaybackProxyView(HomeAssistantView):
reolink_response.reason, reolink_response.reason,
response_headers, response_headers,
) )
response_headers["Content-Type"] = "video/mp4" if "Content-Type" not in response_headers:
response_headers["Content-Type"] = reolink_response.content_type
if response_headers["Content-Type"] == "apolication/octet-stream":
response_headers["Content-Type"] = "application/octet-stream"
response = web.StreamResponse( response = web.StreamResponse(
status=reolink_response.status, status=reolink_response.status,

View File

@@ -12,7 +12,7 @@
"integration_type": "device", "integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "silver", "quality_scale": "silver",
"requirements": ["pysmlight==0.2.4"], "requirements": ["pysmlight==0.2.5"],
"zeroconf": [ "zeroconf": [
{ {
"type": "_slzb-06._tcp.local." "type": "_slzb-06._tcp.local."

View File

@@ -6,9 +6,14 @@ import voluptuous as vol
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE, CONF_NAME, Platform from homeassistant.const import CONF_DEVICE, CONF_NAME, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
async_delete_issue,
)
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import (
@@ -41,6 +46,7 @@ CONFIG_SCHEMA = vol.Schema(
}, },
extra=vol.ALLOW_EXTRA, extra=vol.ALLOW_EXTRA,
) )
DEPRECATED_ISSUE_ID = f"deprecated_system_packages_config_flow_integration_{DOMAIN}"
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
@@ -52,6 +58,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Configure Gammu state machine.""" """Configure Gammu state machine."""
async_create_issue(
hass,
HOMEASSISTANT_DOMAIN,
DEPRECATED_ISSUE_ID,
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_config_flow_integration",
translation_placeholders={
"integration_title": "SMS notifications via GSM-modem",
},
)
device = entry.data[CONF_DEVICE] device = entry.data[CONF_DEVICE]
connection_mode = "at" connection_mode = "at"
@@ -101,4 +120,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
gateway = hass.data[DOMAIN].pop(SMS_GATEWAY)[GATEWAY] gateway = hass.data[DOMAIN].pop(SMS_GATEWAY)[GATEWAY]
await gateway.terminate_async() await gateway.terminate_async()
if not hass.config_entries.async_loaded_entries(DOMAIN):
async_delete_issue(hass, HOMEASSISTANT_DOMAIN, DEPRECATED_ISSUE_ID)
return unload_ok return unload_ok

View File

@@ -7,8 +7,13 @@ import logging
import voluptuous as vol import voluptuous as vol
from homeassistant.components import mqtt from homeassistant.components import mqtt
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import (
DOMAIN as HOMEASSISTANT_DOMAIN,
HomeAssistant,
ServiceCall,
)
from homeassistant.helpers import config_validation as cv, intent from homeassistant.helpers import config_validation as cv, intent
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
DOMAIN = "snips" DOMAIN = "snips"
@@ -91,6 +96,20 @@ SERVICE_SCHEMA_FEEDBACK = vol.Schema(
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Activate Snips component.""" """Activate Snips component."""
async_create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Snips",
},
)
# Make sure MQTT integration is enabled and the client is available # Make sure MQTT integration is enabled and the client is available
if not await mqtt.async_wait_for_mqtt_client(hass): if not await mqtt.async_wait_for_mqtt_client(hass):

View File

@@ -6,5 +6,5 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/sql", "documentation": "https://www.home-assistant.io/integrations/sql",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["SQLAlchemy==2.0.40", "sqlparse==0.5.0"] "requirements": ["SQLAlchemy==2.0.41", "sqlparse==0.5.0"]
} }

View File

@@ -7,7 +7,13 @@ from dataclasses import dataclass, field
from logging import getLogger from logging import getLogger
from aiohttp import web from aiohttp import web
from switchbot_api import CannotConnect, Device, InvalidAuth, Remote, SwitchBotAPI from switchbot_api import (
Device,
Remote,
SwitchBotAPI,
SwitchBotAuthenticationError,
SwitchBotConnectionError,
)
from homeassistant.components import webhook from homeassistant.components import webhook
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@@ -175,12 +181,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
api = SwitchBotAPI(token=token, secret=secret) api = SwitchBotAPI(token=token, secret=secret)
try: try:
devices = await api.list_devices() devices = await api.list_devices()
except InvalidAuth as ex: except SwitchBotAuthenticationError as ex:
_LOGGER.error( _LOGGER.error(
"Invalid authentication while connecting to SwitchBot API: %s", ex "Invalid authentication while connecting to SwitchBot API: %s", ex
) )
return False return False
except CannotConnect as ex: except SwitchBotConnectionError as ex:
raise ConfigEntryNotReady from ex raise ConfigEntryNotReady from ex
_LOGGER.debug("Devices: %s", devices) _LOGGER.debug("Devices: %s", devices)
coordinators_by_id: dict[str, SwitchBotCoordinator] = {} coordinators_by_id: dict[str, SwitchBotCoordinator] = {}

View File

@@ -3,7 +3,11 @@
from logging import getLogger from logging import getLogger
from typing import Any from typing import Any
from switchbot_api import CannotConnect, InvalidAuth, SwitchBotAPI from switchbot_api import (
SwitchBotAPI,
SwitchBotAuthenticationError,
SwitchBotConnectionError,
)
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
@@ -36,9 +40,9 @@ class SwitchBotCloudConfigFlow(ConfigFlow, domain=DOMAIN):
await SwitchBotAPI( await SwitchBotAPI(
token=user_input[CONF_API_TOKEN], secret=user_input[CONF_API_KEY] token=user_input[CONF_API_TOKEN], secret=user_input[CONF_API_KEY]
).list_devices() ).list_devices()
except CannotConnect: except SwitchBotConnectionError:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except InvalidAuth: except SwitchBotAuthenticationError:
errors["base"] = "invalid_auth" errors["base"] = "invalid_auth"
except Exception: except Exception:
_LOGGER.exception("Unexpected exception") _LOGGER.exception("Unexpected exception")

View File

@@ -4,7 +4,7 @@ from asyncio import timeout
from logging import getLogger from logging import getLogger
from typing import Any from typing import Any
from switchbot_api import CannotConnect, Device, Remote, SwitchBotAPI from switchbot_api import Device, Remote, SwitchBotAPI, SwitchBotConnectionError
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -70,5 +70,5 @@ class SwitchBotCoordinator(DataUpdateCoordinator[Status]):
status: Status = await self._api.get_status(self._device_id) status: Status = await self._api.get_status(self._device_id)
_LOGGER.debug("Refreshing %s with %s", self._device_id, status) _LOGGER.debug("Refreshing %s with %s", self._device_id, status)
return status return status
except CannotConnect as err: except SwitchBotConnectionError as err:
raise UpdateFailed(f"Error communicating with API: {err}") from err raise UpdateFailed(f"Error communicating with API: {err}") from err

View File

@@ -8,5 +8,5 @@
"integration_type": "hub", "integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["switchbot_api"], "loggers": ["switchbot_api"],
"requirements": ["switchbot-api==2.3.1"] "requirements": ["switchbot-api==2.4.0"]
} }

View File

@@ -8,5 +8,5 @@
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["aiotedee"], "loggers": ["aiotedee"],
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["aiotedee==0.2.20"] "requirements": ["aiotedee==0.2.23"]
} }

View File

@@ -1 +1,4 @@
"""The tensorflow component.""" """The tensorflow component."""
DOMAIN = "tensorflow"
CONF_GRAPH = "graph"

View File

@@ -26,15 +26,21 @@ from homeassistant.const import (
CONF_SOURCE, CONF_SOURCE,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_START,
) )
from homeassistant.core import HomeAssistant, split_entity_id from homeassistant.core import (
DOMAIN as HOMEASSISTANT_DOMAIN,
HomeAssistant,
split_entity_id,
)
from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util.pil import draw_box from homeassistant.util.pil import draw_box
from . import CONF_GRAPH, DOMAIN
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
DOMAIN = "tensorflow"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ATTR_MATCHES = "matches" ATTR_MATCHES = "matches"
@@ -47,7 +53,6 @@ CONF_BOTTOM = "bottom"
CONF_CATEGORIES = "categories" CONF_CATEGORIES = "categories"
CONF_CATEGORY = "category" CONF_CATEGORY = "category"
CONF_FILE_OUT = "file_out" CONF_FILE_OUT = "file_out"
CONF_GRAPH = "graph"
CONF_LABELS = "labels" CONF_LABELS = "labels"
CONF_LABEL_OFFSET = "label_offset" CONF_LABEL_OFFSET = "label_offset"
CONF_LEFT = "left" CONF_LEFT = "left"
@@ -110,6 +115,21 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the TensorFlow image processing platform.""" """Set up the TensorFlow image processing platform."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Tensorflow",
},
)
model_config = config[CONF_MODEL] model_config = config[CONF_MODEL]
model_dir = model_config.get(CONF_MODEL_DIR) or hass.config.path("tensorflow") model_dir = model_config.get(CONF_MODEL_DIR) or hass.config.path("tensorflow")
labels = model_config.get(CONF_LABELS) or hass.config.path( labels = model_config.get(CONF_LABELS) or hass.config.path(

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/tesla_fleet", "documentation": "https://www.home-assistant.io/integrations/tesla_fleet",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["tesla-fleet-api"], "loggers": ["tesla-fleet-api"],
"requirements": ["tesla-fleet-api==1.0.17"] "requirements": ["tesla-fleet-api==1.1.1"]
} }

View File

@@ -125,6 +125,9 @@ VEHICLE_DESCRIPTIONS: tuple[TeslemetryBinarySensorEntityDescription, ...] = (
key="charge_state_conn_charge_cable", key="charge_state_conn_charge_cable",
polling=True, polling=True,
polling_value_fn=lambda x: x != "<invalid>", polling_value_fn=lambda x: x != "<invalid>",
streaming_listener=lambda vehicle, callback: vehicle.listen_ChargingCableType(
lambda value: callback(value != "Unknown")
),
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
device_class=BinarySensorDeviceClass.CONNECTIVITY, device_class=BinarySensorDeviceClass.CONNECTIVITY,
), ),

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/teslemetry", "documentation": "https://www.home-assistant.io/integrations/teslemetry",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["tesla-fleet-api"], "loggers": ["tesla-fleet-api"],
"requirements": ["tesla-fleet-api==1.0.17", "teslemetry-stream==0.7.9"] "requirements": ["tesla-fleet-api==1.1.1", "teslemetry-stream==0.7.9"]
} }

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/tessie", "documentation": "https://www.home-assistant.io/integrations/tessie",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["tessie", "tesla-fleet-api"], "loggers": ["tessie", "tesla-fleet-api"],
"requirements": ["tessie-api==0.1.1", "tesla-fleet-api==1.0.17"] "requirements": ["tessie-api==0.1.1", "tesla-fleet-api==1.1.1"]
} }

View File

@@ -20,6 +20,10 @@ STATES = {
"Stopped": MediaPlayerState.IDLE, "Stopped": MediaPlayerState.IDLE,
} }
# Tesla uses 31 steps, in 0.333 increments up to 10.333
VOLUME_STEP = 1 / 31
VOLUME_FACTOR = 31 / 3 # 10.333
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0
@@ -38,6 +42,7 @@ class TessieMediaEntity(TessieEntity, MediaPlayerEntity):
"""Vehicle Location Media Class.""" """Vehicle Location Media Class."""
_attr_device_class = MediaPlayerDeviceClass.SPEAKER _attr_device_class = MediaPlayerDeviceClass.SPEAKER
_attr_volume_step = VOLUME_STEP
def __init__( def __init__(
self, self,
@@ -57,9 +62,7 @@ class TessieMediaEntity(TessieEntity, MediaPlayerEntity):
@property @property
def volume_level(self) -> float: def volume_level(self) -> float:
"""Volume level of the media player (0..1).""" """Volume level of the media player (0..1)."""
return self.get("vehicle_state_media_info_audio_volume", 0) / self.get( return self.get("vehicle_state_media_info_audio_volume", 0) / VOLUME_FACTOR
"vehicle_state_media_info_audio_volume_max", 10.333333
)
@property @property
def media_duration(self) -> int | None: def media_duration(self) -> int | None:

View File

@@ -94,21 +94,59 @@ def _get_obj_holidays(
language=language, language=language,
categories=set_categories, categories=set_categories,
) )
supported_languages = obj_holidays.supported_languages
default_language = obj_holidays.default_language
if default_language and not language:
# If no language is set, use the default language
LOGGER.debug("Changing language from None to %s", default_language)
return country_holidays( # Return default if no language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
if ( if (
(supported_languages := obj_holidays.supported_languages) default_language
and language and language
and language not in supported_languages
and language.startswith("en") and language.startswith("en")
): ):
# If language does not match supported languages, use the first English variant
if default_language.startswith("en"):
LOGGER.debug("Changing language from %s to %s", language, default_language)
return country_holidays( # Return default English if default language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
for lang in supported_languages: for lang in supported_languages:
if lang.startswith("en"): if lang.startswith("en"):
obj_holidays = country_holidays( LOGGER.debug("Changing language from %s to %s", language, lang)
return country_holidays(
country, country,
subdiv=province, subdiv=province,
years=year, years=year,
language=lang, language=lang,
categories=set_categories, categories=set_categories,
) )
LOGGER.debug("Changing language from %s to %s", language, lang)
if default_language and language and language not in supported_languages:
# If language does not match supported languages, use the default language
LOGGER.debug("Changing language from %s to %s", language, default_language)
return country_holidays( # Return default English if default language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
return obj_holidays return obj_holidays

View File

@@ -67,8 +67,7 @@ def add_province_and_language_to_schema(
_country = country_holidays(country=country) _country = country_holidays(country=country)
if country_default_language := (_country.default_language): if country_default_language := (_country.default_language):
selectable_languages = _country.supported_languages new_selectable_languages = list(_country.supported_languages)
new_selectable_languages = list(selectable_languages)
language_schema = { language_schema = {
vol.Optional( vol.Optional(
CONF_LANGUAGE, default=country_default_language CONF_LANGUAGE, default=country_default_language
@@ -154,19 +153,7 @@ def validate_custom_dates(user_input: dict[str, Any]) -> None:
years=year, years=year,
language=language, language=language,
) )
if (
(supported_languages := obj_holidays.supported_languages)
and language
and language.startswith("en")
):
for lang in supported_languages:
if lang.startswith("en"):
obj_holidays = country_holidays(
country,
subdiv=province,
years=year,
language=lang,
)
else: else:
obj_holidays = HolidayBase(years=year) obj_holidays = HolidayBase(years=year)

View File

@@ -51,7 +51,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ZimiConfigEntry) -> bool
config_entry_id=entry.entry_id, config_entry_id=entry.entry_id,
identifiers={(DOMAIN, api.mac)}, identifiers={(DOMAIN, api.mac)},
manufacturer=api.brand, manufacturer=api.brand,
name=f"{api.network_name}", name=api.network_name,
model="Zimi Cloud Connect", model="Zimi Cloud Connect",
sw_version=api.firmware_version, sw_version=api.firmware_version,
connections={(CONNECTION_NETWORK_MAC, api.mac)}, connections={(CONNECTION_NETWORK_MAC, api.mac)},

View File

@@ -32,7 +32,7 @@ async def async_setup_entry(
] ]
lights.extend( lights.extend(
[ZimiDimmer(device, api) for device in api.lights if device.type == "dimmer"] ZimiDimmer(device, api) for device in api.lights if device.type == "dimmer"
) )
async_add_entities(lights) async_add_entities(lights)
@@ -81,8 +81,6 @@ class ZimiDimmer(ZimiLight):
super().__init__(device, api) super().__init__(device, api)
self._attr_color_mode = ColorMode.BRIGHTNESS self._attr_color_mode = ColorMode.BRIGHTNESS
self._attr_supported_color_modes = {ColorMode.BRIGHTNESS} self._attr_supported_color_modes = {ColorMode.BRIGHTNESS}
if self._device.type != "dimmer":
raise ValueError("ZimiDimmer needs a dimmable light")
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Instruct the light to turn on (with optional brightness).""" """Instruct the light to turn on (with optional brightness)."""

View File

@@ -26,22 +26,158 @@ DHCP: Final[list[dict[str, str | bool]]] = [
"domain": "airzone", "domain": "airzone",
"macaddress": "E84F25*", "macaddress": "E84F25*",
}, },
{
"domain": "amazon_devices",
"macaddress": "007147*",
},
{
"domain": "amazon_devices",
"macaddress": "00FC8B*",
},
{
"domain": "amazon_devices",
"macaddress": "0812A5*",
},
{
"domain": "amazon_devices",
"macaddress": "086AE5*",
},
{
"domain": "amazon_devices",
"macaddress": "08849D*",
},
{
"domain": "amazon_devices",
"macaddress": "089115*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "08A6BC*", "macaddress": "08A6BC*",
}, },
{
"domain": "amazon_devices",
"macaddress": "08C224*",
},
{
"domain": "amazon_devices",
"macaddress": "0CDC91*",
},
{
"domain": "amazon_devices",
"macaddress": "0CEE99*",
},
{
"domain": "amazon_devices",
"macaddress": "1009F9*",
},
{
"domain": "amazon_devices",
"macaddress": "109693*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "10BF67*", "macaddress": "10BF67*",
}, },
{
"domain": "amazon_devices",
"macaddress": "10CE02*",
},
{
"domain": "amazon_devices",
"macaddress": "140AC5*",
},
{
"domain": "amazon_devices",
"macaddress": "149138*",
},
{
"domain": "amazon_devices",
"macaddress": "1848BE*",
},
{
"domain": "amazon_devices",
"macaddress": "1C12B0*",
},
{
"domain": "amazon_devices",
"macaddress": "1C4D66*",
},
{
"domain": "amazon_devices",
"macaddress": "1C93C4*",
},
{
"domain": "amazon_devices",
"macaddress": "1CFE2B*",
},
{
"domain": "amazon_devices",
"macaddress": "244CE3*",
},
{
"domain": "amazon_devices",
"macaddress": "24CE33*",
},
{
"domain": "amazon_devices",
"macaddress": "2873F6*",
},
{
"domain": "amazon_devices",
"macaddress": "2C71FF*",
},
{
"domain": "amazon_devices",
"macaddress": "34AFB3*",
},
{
"domain": "amazon_devices",
"macaddress": "34D270*",
},
{
"domain": "amazon_devices",
"macaddress": "38F73D*",
},
{
"domain": "amazon_devices",
"macaddress": "3C5CC4*",
},
{
"domain": "amazon_devices",
"macaddress": "3CE441*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "440049*", "macaddress": "440049*",
}, },
{
"domain": "amazon_devices",
"macaddress": "40A2DB*",
},
{
"domain": "amazon_devices",
"macaddress": "40A9CF*",
},
{
"domain": "amazon_devices",
"macaddress": "40B4CD*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "443D54*", "macaddress": "443D54*",
}, },
{
"domain": "amazon_devices",
"macaddress": "44650D*",
},
{
"domain": "amazon_devices",
"macaddress": "485F2D*",
},
{
"domain": "amazon_devices",
"macaddress": "48785E*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "48B423*", "macaddress": "48B423*",
@@ -50,6 +186,14 @@ DHCP: Final[list[dict[str, str | bool]]] = [
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "4C1744*", "macaddress": "4C1744*",
}, },
{
"domain": "amazon_devices",
"macaddress": "4CEFC0*",
},
{
"domain": "amazon_devices",
"macaddress": "5007C3*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "50D45C*", "macaddress": "50D45C*",
@@ -58,6 +202,34 @@ DHCP: Final[list[dict[str, str | bool]]] = [
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "50DCE7*", "macaddress": "50DCE7*",
}, },
{
"domain": "amazon_devices",
"macaddress": "50F5DA*",
},
{
"domain": "amazon_devices",
"macaddress": "5C415A*",
},
{
"domain": "amazon_devices",
"macaddress": "6837E9*",
},
{
"domain": "amazon_devices",
"macaddress": "6854FD*",
},
{
"domain": "amazon_devices",
"macaddress": "689A87*",
},
{
"domain": "amazon_devices",
"macaddress": "68B691*",
},
{
"domain": "amazon_devices",
"macaddress": "68DBF5*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "68F63B*", "macaddress": "68F63B*",
@@ -66,18 +238,70 @@ DHCP: Final[list[dict[str, str | bool]]] = [
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "6C0C9A*", "macaddress": "6C0C9A*",
}, },
{
"domain": "amazon_devices",
"macaddress": "6C5697*",
},
{
"domain": "amazon_devices",
"macaddress": "7458F3*",
},
{
"domain": "amazon_devices",
"macaddress": "74C246*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "74D637*", "macaddress": "74D637*",
}, },
{
"domain": "amazon_devices",
"macaddress": "74E20C*",
},
{
"domain": "amazon_devices",
"macaddress": "74ECB2*",
},
{
"domain": "amazon_devices",
"macaddress": "786C84*",
},
{
"domain": "amazon_devices",
"macaddress": "78A03F*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "7C6166*", "macaddress": "7C6166*",
}, },
{
"domain": "amazon_devices",
"macaddress": "7C6305*",
},
{
"domain": "amazon_devices",
"macaddress": "7CD566*",
},
{
"domain": "amazon_devices",
"macaddress": "8871E5*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "901195*", "macaddress": "901195*",
}, },
{
"domain": "amazon_devices",
"macaddress": "90235B*",
},
{
"domain": "amazon_devices",
"macaddress": "90A822*",
},
{
"domain": "amazon_devices",
"macaddress": "90F82E*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "943A91*", "macaddress": "943A91*",
@@ -86,30 +310,154 @@ DHCP: Final[list[dict[str, str | bool]]] = [
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "98226E*", "macaddress": "98226E*",
}, },
{
"domain": "amazon_devices",
"macaddress": "98CCF3*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "9CC8E9*", "macaddress": "9CC8E9*",
}, },
{
"domain": "amazon_devices",
"macaddress": "A002DC*",
},
{
"domain": "amazon_devices",
"macaddress": "A0D2B1*",
},
{
"domain": "amazon_devices",
"macaddress": "A40801*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "A8E621*", "macaddress": "A8E621*",
}, },
{
"domain": "amazon_devices",
"macaddress": "AC416A*",
},
{
"domain": "amazon_devices",
"macaddress": "AC63BE*",
},
{
"domain": "amazon_devices",
"macaddress": "ACCCFC*",
},
{
"domain": "amazon_devices",
"macaddress": "B0739C*",
},
{
"domain": "amazon_devices",
"macaddress": "B0CFCB*",
},
{
"domain": "amazon_devices",
"macaddress": "B0F7C4*",
},
{
"domain": "amazon_devices",
"macaddress": "B85F98*",
},
{
"domain": "amazon_devices",
"macaddress": "C091B9*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "C095CF*", "macaddress": "C095CF*",
}, },
{
"domain": "amazon_devices",
"macaddress": "C49500*",
},
{
"domain": "amazon_devices",
"macaddress": "C86C3D*",
},
{
"domain": "amazon_devices",
"macaddress": "CC9EA2*",
},
{
"domain": "amazon_devices",
"macaddress": "CCF735*",
},
{
"domain": "amazon_devices",
"macaddress": "DC54D7*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "D8BE65*", "macaddress": "D8BE65*",
}, },
{
"domain": "amazon_devices",
"macaddress": "D8FBD6*",
},
{
"domain": "amazon_devices",
"macaddress": "DC91BF*",
},
{
"domain": "amazon_devices",
"macaddress": "DCA0D0*",
},
{
"domain": "amazon_devices",
"macaddress": "E0F728*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "EC2BEB*", "macaddress": "EC2BEB*",
}, },
{
"domain": "amazon_devices",
"macaddress": "EC8AC4*",
},
{
"domain": "amazon_devices",
"macaddress": "ECA138*",
},
{ {
"domain": "amazon_devices", "domain": "amazon_devices",
"macaddress": "F02F9E*", "macaddress": "F02F9E*",
}, },
{
"domain": "amazon_devices",
"macaddress": "F0272D*",
},
{
"domain": "amazon_devices",
"macaddress": "F0F0A4*",
},
{
"domain": "amazon_devices",
"macaddress": "F4032A*",
},
{
"domain": "amazon_devices",
"macaddress": "F854B8*",
},
{
"domain": "amazon_devices",
"macaddress": "FC492D*",
},
{
"domain": "amazon_devices",
"macaddress": "FC65DE*",
},
{
"domain": "amazon_devices",
"macaddress": "FCA183*",
},
{
"domain": "amazon_devices",
"macaddress": "FCE9D8*",
},
{ {
"domain": "august", "domain": "august",
"hostname": "connect", "hostname": "connect",

View File

@@ -5867,10 +5867,18 @@
"iot_class": "local_push" "iot_class": "local_push"
}, },
"shelly": { "shelly": {
"name": "Shelly", "name": "shelly",
"integration_type": "device", "integrations": {
"config_flow": true, "shelly": {
"iot_class": "local_push" "integration_type": "device",
"config_flow": true,
"iot_class": "local_push",
"name": "Shelly"
}
},
"iot_standards": [
"zwave"
]
}, },
"shodan": { "shodan": {
"name": "Shodan", "name": "Shodan",

View File

@@ -138,6 +138,8 @@ class DataUpdateCoordinator(BaseDataUpdateCoordinatorProtocol, Generic[_DataT]):
async def _on_hass_stop(_: Event) -> None: async def _on_hass_stop(_: Event) -> None:
"""Shutdown coordinator on HomeAssistant stop.""" """Shutdown coordinator on HomeAssistant stop."""
# Already cleared on EVENT_HOMEASSISTANT_STOP, via async_fire_internal
self._unsub_shutdown = None
await self.async_shutdown() await self.async_shutdown()
self._unsub_shutdown = self.hass.bus.async_listen_once( self._unsub_shutdown = self.hass.bus.async_listen_once(

View File

@@ -6,7 +6,7 @@ aiodns==3.4.0
aiohasupervisor==0.3.1 aiohasupervisor==0.3.1
aiohttp-asyncmdnsresolver==0.1.1 aiohttp-asyncmdnsresolver==0.1.1
aiohttp-fast-zlib==0.2.3 aiohttp-fast-zlib==0.2.3
aiohttp==3.12.2 aiohttp==3.12.6
aiohttp_cors==0.7.0 aiohttp_cors==0.7.0
aiousbwatcher==1.1.1 aiousbwatcher==1.1.1
aiozoneinfo==0.2.3 aiozoneinfo==0.2.3
@@ -38,8 +38,8 @@ habluetooth==3.48.2
hass-nabucasa==0.101.0 hass-nabucasa==0.101.0
hassil==2.2.3 hassil==2.2.3
home-assistant-bluetooth==1.13.1 home-assistant-bluetooth==1.13.1
home-assistant-frontend==20250527.0 home-assistant-frontend==20250531.0
home-assistant-intents==2025.5.7 home-assistant-intents==2025.5.28
httpx==0.28.1 httpx==0.28.1
ifaddr==0.2.0 ifaddr==0.2.0
Jinja2==3.1.6 Jinja2==3.1.6
@@ -63,7 +63,7 @@ PyTurboJPEG==1.7.5
PyYAML==6.0.2 PyYAML==6.0.2
requests==2.32.3 requests==2.32.3
securetar==2025.2.1 securetar==2025.2.1
SQLAlchemy==2.0.40 SQLAlchemy==2.0.41
standard-aifc==3.13.0 standard-aifc==3.13.0
standard-telnetlib==3.13.0 standard-telnetlib==3.13.0
typing-extensions>=4.13.0,<5.0 typing-extensions>=4.13.0,<5.0
@@ -111,8 +111,8 @@ uuid==1000000000.0.0
# even newer versions seem to introduce new issues, it's useful for us to pin all these # even newer versions seem to introduce new issues, it's useful for us to pin all these
# requirements so we can directly link HA versions to these library versions. # requirements so we can directly link HA versions to these library versions.
anyio==4.9.0 anyio==4.9.0
h11==0.14.0 h11==0.16.0
httpcore==1.0.7 httpcore==1.0.9
# Ensure we have a hyperframe version that works in Python 3.10 # Ensure we have a hyperframe version that works in Python 3.10
# 5.2.0 fixed a collections abc deprecation # 5.2.0 fixed a collections abc deprecation

View File

@@ -28,7 +28,7 @@ dependencies = [
# change behavior based on presence of supervisor. Deprecated with #127228 # change behavior based on presence of supervisor. Deprecated with #127228
# Lib can be removed with 2025.11 # Lib can be removed with 2025.11
"aiohasupervisor==0.3.1", "aiohasupervisor==0.3.1",
"aiohttp==3.12.2", "aiohttp==3.12.6",
"aiohttp_cors==0.7.0", "aiohttp_cors==0.7.0",
"aiohttp-fast-zlib==0.2.3", "aiohttp-fast-zlib==0.2.3",
"aiohttp-asyncmdnsresolver==0.1.1", "aiohttp-asyncmdnsresolver==0.1.1",
@@ -66,7 +66,7 @@ dependencies = [
# onboarding->cloud->assist_pipeline->conversation->home_assistant_intents. Onboarding needs # onboarding->cloud->assist_pipeline->conversation->home_assistant_intents. Onboarding needs
# to be setup in stage 0, but we don't want to also promote cloud with all its # to be setup in stage 0, but we don't want to also promote cloud with all its
# dependencies to stage 0. # dependencies to stage 0.
"home-assistant-intents==2025.5.7", "home-assistant-intents==2025.5.28",
"ifaddr==0.2.0", "ifaddr==0.2.0",
"Jinja2==3.1.6", "Jinja2==3.1.6",
"lru-dict==1.3.0", "lru-dict==1.3.0",
@@ -108,7 +108,7 @@ dependencies = [
"PyYAML==6.0.2", "PyYAML==6.0.2",
"requests==2.32.3", "requests==2.32.3",
"securetar==2025.2.1", "securetar==2025.2.1",
"SQLAlchemy==2.0.40", "SQLAlchemy==2.0.41",
"standard-aifc==3.13.0", "standard-aifc==3.13.0",
"standard-telnetlib==3.13.0", "standard-telnetlib==3.13.0",
"typing-extensions>=4.13.0,<5.0", "typing-extensions>=4.13.0,<5.0",

6
requirements.txt generated
View File

@@ -5,7 +5,7 @@
# Home Assistant Core # Home Assistant Core
aiodns==3.4.0 aiodns==3.4.0
aiohasupervisor==0.3.1 aiohasupervisor==0.3.1
aiohttp==3.12.2 aiohttp==3.12.6
aiohttp_cors==0.7.0 aiohttp_cors==0.7.0
aiohttp-fast-zlib==0.2.3 aiohttp-fast-zlib==0.2.3
aiohttp-asyncmdnsresolver==0.1.1 aiohttp-asyncmdnsresolver==0.1.1
@@ -27,7 +27,7 @@ hass-nabucasa==0.101.0
hassil==2.2.3 hassil==2.2.3
httpx==0.28.1 httpx==0.28.1
home-assistant-bluetooth==1.13.1 home-assistant-bluetooth==1.13.1
home-assistant-intents==2025.5.7 home-assistant-intents==2025.5.28
ifaddr==0.2.0 ifaddr==0.2.0
Jinja2==3.1.6 Jinja2==3.1.6
lru-dict==1.3.0 lru-dict==1.3.0
@@ -48,7 +48,7 @@ PyTurboJPEG==1.7.5
PyYAML==6.0.2 PyYAML==6.0.2
requests==2.32.3 requests==2.32.3
securetar==2025.2.1 securetar==2025.2.1
SQLAlchemy==2.0.40 SQLAlchemy==2.0.41
standard-aifc==3.13.0 standard-aifc==3.13.0
standard-telnetlib==3.13.0 standard-telnetlib==3.13.0
typing-extensions>=4.13.0,<5.0 typing-extensions>=4.13.0,<5.0

34
requirements_all.txt generated
View File

@@ -113,7 +113,7 @@ RtmAPI==0.7.2
# homeassistant.components.recorder # homeassistant.components.recorder
# homeassistant.components.sql # homeassistant.components.sql
SQLAlchemy==2.0.40 SQLAlchemy==2.0.41
# homeassistant.components.tami4 # homeassistant.components.tami4
Tami4EdgeAPI==3.0 Tami4EdgeAPI==3.0
@@ -265,7 +265,7 @@ aioharmony==0.5.2
aiohasupervisor==0.3.1 aiohasupervisor==0.3.1
# homeassistant.components.home_connect # homeassistant.components.home_connect
aiohomeconnect==0.17.0 aiohomeconnect==0.17.1
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==3.2.14 aiohomekit==3.2.14
@@ -280,7 +280,7 @@ aiohue==4.7.4
aioimaplib==2.0.1 aioimaplib==2.0.1
# homeassistant.components.immich # homeassistant.components.immich
aioimmich==0.6.0 aioimmich==0.7.0
# homeassistant.components.apache_kafka # homeassistant.components.apache_kafka
aiokafka==0.10.0 aiokafka==0.10.0
@@ -405,7 +405,7 @@ aiosyncthing==0.5.1
aiotankerkoenig==0.4.2 aiotankerkoenig==0.4.2
# homeassistant.components.tedee # homeassistant.components.tedee
aiotedee==0.2.20 aiotedee==0.2.23
# homeassistant.components.tractive # homeassistant.components.tractive
aiotractive==0.6.0 aiotractive==0.6.0
@@ -759,7 +759,7 @@ dbus-fast==2.43.0
debugpy==1.8.14 debugpy==1.8.14
# homeassistant.components.decora_wifi # homeassistant.components.decora_wifi
# decora-wifi==1.4 decora-wifi==1.4
# homeassistant.components.decora # homeassistant.components.decora
# decora==0.6 # decora==0.6
@@ -1164,10 +1164,10 @@ hole==0.8.0
holidays==0.73 holidays==0.73
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20250527.0 home-assistant-frontend==20250531.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2025.5.7 home-assistant-intents==2025.5.28
# homeassistant.components.homematicip_cloud # homeassistant.components.homematicip_cloud
homematicip==2.0.1.1 homematicip==2.0.1.1
@@ -1617,7 +1617,7 @@ openwrt-luci-rpc==1.1.17
openwrt-ubus-rpc==0.0.2 openwrt-ubus-rpc==0.0.2
# homeassistant.components.opower # homeassistant.components.opower
opower==0.12.2 opower==0.12.3
# homeassistant.components.oralb # homeassistant.components.oralb
oralb-ble==0.17.6 oralb-ble==0.17.6
@@ -1832,7 +1832,7 @@ pyairnow==1.2.1
pyairvisual==2023.08.1 pyairvisual==2023.08.1
# homeassistant.components.aprilaire # homeassistant.components.aprilaire
pyaprilaire==0.9.0 pyaprilaire==0.9.1
# homeassistant.components.asuswrt # homeassistant.components.asuswrt
pyasuswrt==0.1.21 pyasuswrt==0.1.21
@@ -2051,7 +2051,7 @@ pyiqvia==2022.04.0
pyirishrail==0.0.2 pyirishrail==0.0.2
# homeassistant.components.iskra # homeassistant.components.iskra
pyiskra==0.1.15 pyiskra==0.1.19
# homeassistant.components.iss # homeassistant.components.iss
pyiss==1.0.1 pyiss==1.0.1
@@ -2096,7 +2096,7 @@ pykwb==0.0.8
pylacrosse==0.4 pylacrosse==0.4
# homeassistant.components.lamarzocco # homeassistant.components.lamarzocco
pylamarzocco==2.0.7 pylamarzocco==2.0.8
# homeassistant.components.lastfm # homeassistant.components.lastfm
pylast==5.1.0 pylast==5.1.0
@@ -2251,7 +2251,7 @@ pyplaato==0.0.19
pypoint==3.0.0 pypoint==3.0.0
# homeassistant.components.probe_plus # homeassistant.components.probe_plus
pyprobeplus==1.0.0 pyprobeplus==1.0.1
# homeassistant.components.profiler # homeassistant.components.profiler
pyprof2calltree==1.4.5 pyprof2calltree==1.4.5
@@ -2353,7 +2353,7 @@ pysmhi==1.0.2
pysml==0.0.12 pysml==0.0.12
# homeassistant.components.smlight # homeassistant.components.smlight
pysmlight==0.2.4 pysmlight==0.2.5
# homeassistant.components.snmp # homeassistant.components.snmp
pysnmp==6.2.6 pysnmp==6.2.6
@@ -2452,7 +2452,7 @@ python-juicenet==1.1.0
python-kasa[speedups]==0.10.2 python-kasa[speedups]==0.10.2
# homeassistant.components.linkplay # homeassistant.components.linkplay
python-linkplay==0.2.8 python-linkplay==0.2.9
# homeassistant.components.lirc # homeassistant.components.lirc
# python-lirc==1.2.3 # python-lirc==1.2.3
@@ -2652,7 +2652,7 @@ renault-api==0.3.1
renson-endura-delta==1.7.2 renson-endura-delta==1.7.2
# homeassistant.components.reolink # homeassistant.components.reolink
reolink-aio==0.13.3 reolink-aio==0.13.4
# homeassistant.components.idteck_prox # homeassistant.components.idteck_prox
rfk101py==0.0.1 rfk101py==0.0.1
@@ -2859,7 +2859,7 @@ surepy==0.9.0
swisshydrodata==0.1.0 swisshydrodata==0.1.0
# homeassistant.components.switchbot_cloud # homeassistant.components.switchbot_cloud
switchbot-api==2.3.1 switchbot-api==2.4.0
# homeassistant.components.synology_srm # homeassistant.components.synology_srm
synology-srm==0.2.0 synology-srm==0.2.0
@@ -2900,7 +2900,7 @@ temperusb==1.6.1
# homeassistant.components.tesla_fleet # homeassistant.components.tesla_fleet
# homeassistant.components.teslemetry # homeassistant.components.teslemetry
# homeassistant.components.tessie # homeassistant.components.tessie
tesla-fleet-api==1.0.17 tesla-fleet-api==1.1.1
# homeassistant.components.powerwall # homeassistant.components.powerwall
tesla-powerwall==0.5.2 tesla-powerwall==0.5.2

View File

@@ -107,7 +107,7 @@ RtmAPI==0.7.2
# homeassistant.components.recorder # homeassistant.components.recorder
# homeassistant.components.sql # homeassistant.components.sql
SQLAlchemy==2.0.40 SQLAlchemy==2.0.41
# homeassistant.components.tami4 # homeassistant.components.tami4
Tami4EdgeAPI==3.0 Tami4EdgeAPI==3.0
@@ -250,7 +250,7 @@ aioharmony==0.5.2
aiohasupervisor==0.3.1 aiohasupervisor==0.3.1
# homeassistant.components.home_connect # homeassistant.components.home_connect
aiohomeconnect==0.17.0 aiohomeconnect==0.17.1
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==3.2.14 aiohomekit==3.2.14
@@ -265,7 +265,7 @@ aiohue==4.7.4
aioimaplib==2.0.1 aioimaplib==2.0.1
# homeassistant.components.immich # homeassistant.components.immich
aioimmich==0.6.0 aioimmich==0.7.0
# homeassistant.components.apache_kafka # homeassistant.components.apache_kafka
aiokafka==0.10.0 aiokafka==0.10.0
@@ -387,7 +387,7 @@ aiosyncthing==0.5.1
aiotankerkoenig==0.4.2 aiotankerkoenig==0.4.2
# homeassistant.components.tedee # homeassistant.components.tedee
aiotedee==0.2.20 aiotedee==0.2.23
# homeassistant.components.tractive # homeassistant.components.tractive
aiotractive==0.6.0 aiotractive==0.6.0
@@ -561,6 +561,9 @@ bluecurrent-api==1.2.3
# homeassistant.components.bluemaestro # homeassistant.components.bluemaestro
bluemaestro-ble==0.4.1 bluemaestro-ble==0.4.1
# homeassistant.components.decora
# bluepy==1.3.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bluetooth-adapters==0.21.4 bluetooth-adapters==0.21.4
@@ -655,6 +658,9 @@ dbus-fast==2.43.0
# homeassistant.components.debugpy # homeassistant.components.debugpy
debugpy==1.8.14 debugpy==1.8.14
# homeassistant.components.decora
# decora==0.6
# homeassistant.components.ecovacs # homeassistant.components.ecovacs
deebot-client==13.2.1 deebot-client==13.2.1
@@ -1001,10 +1007,10 @@ hole==0.8.0
holidays==0.73 holidays==0.73
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20250527.0 home-assistant-frontend==20250531.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2025.5.7 home-assistant-intents==2025.5.28
# homeassistant.components.homematicip_cloud # homeassistant.components.homematicip_cloud
homematicip==2.0.1.1 homematicip==2.0.1.1
@@ -1361,7 +1367,7 @@ openhomedevice==2.2.0
openwebifpy==4.3.1 openwebifpy==4.3.1
# homeassistant.components.opower # homeassistant.components.opower
opower==0.12.2 opower==0.12.3
# homeassistant.components.oralb # homeassistant.components.oralb
oralb-ble==0.17.6 oralb-ble==0.17.6
@@ -1525,7 +1531,7 @@ pyairnow==1.2.1
pyairvisual==2023.08.1 pyairvisual==2023.08.1
# homeassistant.components.aprilaire # homeassistant.components.aprilaire
pyaprilaire==0.9.0 pyaprilaire==0.9.1
# homeassistant.components.asuswrt # homeassistant.components.asuswrt
pyasuswrt==0.1.21 pyasuswrt==0.1.21
@@ -1560,6 +1566,9 @@ pybravia==0.3.4
# homeassistant.components.cloudflare # homeassistant.components.cloudflare
pycfdns==3.0.0 pycfdns==3.0.0
# homeassistant.components.tensorflow
# pycocotools==2.0.6
# homeassistant.components.comfoconnect # homeassistant.components.comfoconnect
pycomfoconnect==0.5.1 pycomfoconnect==0.5.1
@@ -1690,7 +1699,7 @@ pyipp==0.17.0
pyiqvia==2022.04.0 pyiqvia==2022.04.0
# homeassistant.components.iskra # homeassistant.components.iskra
pyiskra==0.1.15 pyiskra==0.1.19
# homeassistant.components.iss # homeassistant.components.iss
pyiss==1.0.1 pyiss==1.0.1
@@ -1726,7 +1735,7 @@ pykrakenapi==0.1.8
pykulersky==0.5.8 pykulersky==0.5.8
# homeassistant.components.lamarzocco # homeassistant.components.lamarzocco
pylamarzocco==2.0.7 pylamarzocco==2.0.8
# homeassistant.components.lastfm # homeassistant.components.lastfm
pylast==5.1.0 pylast==5.1.0
@@ -1860,7 +1869,7 @@ pyplaato==0.0.19
pypoint==3.0.0 pypoint==3.0.0
# homeassistant.components.probe_plus # homeassistant.components.probe_plus
pyprobeplus==1.0.0 pyprobeplus==1.0.1
# homeassistant.components.profiler # homeassistant.components.profiler
pyprof2calltree==1.4.5 pyprof2calltree==1.4.5
@@ -1941,7 +1950,7 @@ pysmhi==1.0.2
pysml==0.0.12 pysml==0.0.12
# homeassistant.components.smlight # homeassistant.components.smlight
pysmlight==0.2.4 pysmlight==0.2.5
# homeassistant.components.snmp # homeassistant.components.snmp
pysnmp==6.2.6 pysnmp==6.2.6
@@ -2010,7 +2019,10 @@ python-juicenet==1.1.0
python-kasa[speedups]==0.10.2 python-kasa[speedups]==0.10.2
# homeassistant.components.linkplay # homeassistant.components.linkplay
python-linkplay==0.2.8 python-linkplay==0.2.9
# homeassistant.components.lirc
# python-lirc==1.2.3
# homeassistant.components.matter # homeassistant.components.matter
python-matter-server==7.0.0 python-matter-server==7.0.0
@@ -2095,6 +2107,9 @@ pytrydan==0.8.0
# homeassistant.components.uptimerobot # homeassistant.components.uptimerobot
pyuptimerobot==22.2.0 pyuptimerobot==22.2.0
# homeassistant.components.keyboard
# pyuserinput==0.1.11
# homeassistant.components.vera # homeassistant.components.vera
pyvera==0.3.15 pyvera==0.3.15
@@ -2177,7 +2192,7 @@ renault-api==0.3.1
renson-endura-delta==1.7.2 renson-endura-delta==1.7.2
# homeassistant.components.reolink # homeassistant.components.reolink
reolink-aio==0.13.3 reolink-aio==0.13.4
# homeassistant.components.rflink # homeassistant.components.rflink
rflink==0.0.66 rflink==0.0.66
@@ -2339,7 +2354,7 @@ subarulink==0.7.13
surepy==0.9.0 surepy==0.9.0
# homeassistant.components.switchbot_cloud # homeassistant.components.switchbot_cloud
switchbot-api==2.3.1 switchbot-api==2.4.0
# homeassistant.components.system_bridge # homeassistant.components.system_bridge
systembridgeconnector==4.1.5 systembridgeconnector==4.1.5
@@ -2359,10 +2374,13 @@ temescal==0.5
# homeassistant.components.temper # homeassistant.components.temper
temperusb==1.6.1 temperusb==1.6.1
# homeassistant.components.tensorflow
# tensorflow==2.5.0
# homeassistant.components.tesla_fleet # homeassistant.components.tesla_fleet
# homeassistant.components.teslemetry # homeassistant.components.teslemetry
# homeassistant.components.tessie # homeassistant.components.tessie
tesla-fleet-api==1.0.17 tesla-fleet-api==1.1.1
# homeassistant.components.powerwall # homeassistant.components.powerwall
tesla-powerwall==0.5.2 tesla-powerwall==0.5.2
@@ -2376,6 +2394,9 @@ teslemetry-stream==0.7.9
# homeassistant.components.tessie # homeassistant.components.tessie
tessie-api==0.1.1 tessie-api==0.1.1
# homeassistant.components.tensorflow
# tf-models-official==2.5.0
# homeassistant.components.thermobeacon # homeassistant.components.thermobeacon
thermobeacon-ble==0.10.0 thermobeacon-ble==0.10.0

View File

@@ -27,7 +27,6 @@ EXCLUDED_REQUIREMENTS_ALL = {
"beewi-smartclim", # depends on bluepy "beewi-smartclim", # depends on bluepy
"bluepy", "bluepy",
"decora", "decora",
"decora-wifi",
"evdev", "evdev",
"face-recognition", "face-recognition",
"pybluez", "pybluez",
@@ -43,7 +42,6 @@ EXCLUDED_REQUIREMENTS_ALL = {
# Requirements excluded by EXCLUDED_REQUIREMENTS_ALL which should be included when # Requirements excluded by EXCLUDED_REQUIREMENTS_ALL which should be included when
# building integration wheels for all architectures. # building integration wheels for all architectures.
INCLUDED_REQUIREMENTS_WHEELS = { INCLUDED_REQUIREMENTS_WHEELS = {
"decora-wifi",
"evdev", "evdev",
"pycups", "pycups",
"python-gammu", "python-gammu",
@@ -138,8 +136,8 @@ uuid==1000000000.0.0
# even newer versions seem to introduce new issues, it's useful for us to pin all these # even newer versions seem to introduce new issues, it's useful for us to pin all these
# requirements so we can directly link HA versions to these library versions. # requirements so we can directly link HA versions to these library versions.
anyio==4.9.0 anyio==4.9.0
h11==0.14.0 h11==0.16.0
httpcore==1.0.7 httpcore==1.0.9
# Ensure we have a hyperframe version that works in Python 3.10 # Ensure we have a hyperframe version that works in Python 3.10
# 5.2.0 fixed a collections abc deprecation # 5.2.0 fixed a collections abc deprecation

View File

@@ -25,7 +25,7 @@ RUN --mount=from=ghcr.io/astral-sh/uv:0.7.1,source=/uv,target=/bin/uv \
-c /usr/src/homeassistant/homeassistant/package_constraints.txt \ -c /usr/src/homeassistant/homeassistant/package_constraints.txt \
-r /usr/src/homeassistant/requirements.txt \ -r /usr/src/homeassistant/requirements.txt \
stdlib-list==0.10.0 pipdeptree==2.26.1 tqdm==4.67.1 ruff==0.11.0 \ stdlib-list==0.10.0 pipdeptree==2.26.1 tqdm==4.67.1 ruff==0.11.0 \
PyTurboJPEG==1.7.5 go2rtc-client==0.1.3b0 ha-ffmpeg==3.2.2 hassil==2.2.3 home-assistant-intents==2025.5.7 mutagen==1.47.0 pymicro-vad==1.0.1 pyspeex-noise==1.0.2 PyTurboJPEG==1.7.5 go2rtc-client==0.1.3b0 ha-ffmpeg==3.2.2 hassil==2.2.3 home-assistant-intents==2025.5.28 mutagen==1.47.0 pymicro-vad==1.0.1 pyspeex-noise==1.0.2
LABEL "name"="hassfest" LABEL "name"="hassfest"
LABEL "maintainer"="Home Assistant <hello@home-assistant.io>" LABEL "maintainer"="Home Assistant <hello@home-assistant.io>"

View File

@@ -2,7 +2,7 @@
from unittest.mock import patch from unittest.mock import patch
from homeassistant.components.abode import DOMAIN as ABODE_DOMAIN from homeassistant.components.abode import DOMAIN
from homeassistant.components.abode.const import CONF_POLLING from homeassistant.components.abode.const import CONF_POLLING
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -14,7 +14,7 @@ from tests.common import MockConfigEntry
async def setup_platform(hass: HomeAssistant, platform: str) -> MockConfigEntry: async def setup_platform(hass: HomeAssistant, platform: str) -> MockConfigEntry:
"""Set up the Abode platform.""" """Set up the Abode platform."""
mock_entry = MockConfigEntry( mock_entry = MockConfigEntry(
domain=ABODE_DOMAIN, domain=DOMAIN,
data={ data={
CONF_USERNAME: "user@email.com", CONF_USERNAME: "user@email.com",
CONF_PASSWORD: "password", CONF_PASSWORD: "password",
@@ -27,7 +27,7 @@ async def setup_platform(hass: HomeAssistant, platform: str) -> MockConfigEntry:
patch("homeassistant.components.abode.PLATFORMS", [platform]), patch("homeassistant.components.abode.PLATFORMS", [platform]),
patch("jaraco.abode.event_controller.sio"), patch("jaraco.abode.event_controller.sio"),
): ):
assert await async_setup_component(hass, ABODE_DOMAIN, {}) assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done() await hass.async_block_till_done()
return mock_entry return mock_entry

View File

@@ -2,7 +2,7 @@
from unittest.mock import patch from unittest.mock import patch
from homeassistant.components.abode.const import DOMAIN as ABODE_DOMAIN from homeassistant.components.abode.const import DOMAIN
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN, CameraState from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN, CameraState
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -35,7 +35,7 @@ async def test_capture_image(hass: HomeAssistant) -> None:
with patch("jaraco.abode.devices.camera.Camera.capture") as mock_capture: with patch("jaraco.abode.devices.camera.Camera.capture") as mock_capture:
await hass.services.async_call( await hass.services.async_call(
ABODE_DOMAIN, DOMAIN,
"capture_image", "capture_image",
{ATTR_ENTITY_ID: "camera.test_cam"}, {ATTR_ENTITY_ID: "camera.test_cam"},
blocking=True, blocking=True,

View File

@@ -8,7 +8,7 @@ from jaraco.abode.exceptions import (
Exception as AbodeException, Exception as AbodeException,
) )
from homeassistant.components.abode import DOMAIN as ABODE_DOMAIN, SERVICE_SETTINGS from homeassistant.components.abode import DOMAIN, SERVICE_SETTINGS
from homeassistant.components.alarm_control_panel import DOMAIN as ALARM_DOMAIN from homeassistant.components.alarm_control_panel import DOMAIN as ALARM_DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_USERNAME from homeassistant.const import CONF_USERNAME
@@ -23,7 +23,7 @@ async def test_change_settings(hass: HomeAssistant) -> None:
with patch("jaraco.abode.client.Client.set_setting") as mock_set_setting: with patch("jaraco.abode.client.Client.set_setting") as mock_set_setting:
await hass.services.async_call( await hass.services.async_call(
ABODE_DOMAIN, DOMAIN,
SERVICE_SETTINGS, SERVICE_SETTINGS,
{"setting": "confirm_snd", "value": "loud"}, {"setting": "confirm_snd", "value": "loud"},
blocking=True, blocking=True,

View File

@@ -2,10 +2,7 @@
from unittest.mock import patch from unittest.mock import patch
from homeassistant.components.abode import ( from homeassistant.components.abode import DOMAIN, SERVICE_TRIGGER_AUTOMATION
DOMAIN as ABODE_DOMAIN,
SERVICE_TRIGGER_AUTOMATION,
)
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
@@ -119,7 +116,7 @@ async def test_trigger_automation(hass: HomeAssistant) -> None:
with patch("jaraco.abode.automation.Automation.trigger") as mock: with patch("jaraco.abode.automation.Automation.trigger") as mock:
await hass.services.async_call( await hass.services.async_call(
ABODE_DOMAIN, DOMAIN,
SERVICE_TRIGGER_AUTOMATION, SERVICE_TRIGGER_AUTOMATION,
{ATTR_ENTITY_ID: AUTOMATION_ID}, {ATTR_ENTITY_ID: AUTOMATION_ID},
blocking=True, blocking=True,

View File

@@ -3,7 +3,7 @@
from datetime import timedelta from datetime import timedelta
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
from homeassistant.components.advantage_air.const import DOMAIN as ADVANTAGE_AIR_DOMAIN from homeassistant.components.advantage_air.const import DOMAIN
from homeassistant.components.advantage_air.sensor import ( from homeassistant.components.advantage_air.sensor import (
ADVANTAGE_AIR_SERVICE_SET_TIME_TO, ADVANTAGE_AIR_SERVICE_SET_TIME_TO,
ADVANTAGE_AIR_SET_COUNTDOWN_VALUE, ADVANTAGE_AIR_SET_COUNTDOWN_VALUE,
@@ -41,7 +41,7 @@ async def test_sensor_platform(
value = 20 value = 20
await hass.services.async_call( await hass.services.async_call(
ADVANTAGE_AIR_DOMAIN, DOMAIN,
ADVANTAGE_AIR_SERVICE_SET_TIME_TO, ADVANTAGE_AIR_SERVICE_SET_TIME_TO,
{ATTR_ENTITY_ID: [entity_id], ADVANTAGE_AIR_SET_COUNTDOWN_VALUE: value}, {ATTR_ENTITY_ID: [entity_id], ADVANTAGE_AIR_SET_COUNTDOWN_VALUE: value},
blocking=True, blocking=True,
@@ -61,7 +61,7 @@ async def test_sensor_platform(
value = 0 value = 0
await hass.services.async_call( await hass.services.async_call(
ADVANTAGE_AIR_DOMAIN, DOMAIN,
ADVANTAGE_AIR_SERVICE_SET_TIME_TO, ADVANTAGE_AIR_SERVICE_SET_TIME_TO,
{ATTR_ENTITY_ID: [entity_id], ADVANTAGE_AIR_SET_COUNTDOWN_VALUE: value}, {ATTR_ENTITY_ID: [entity_id], ADVANTAGE_AIR_SET_COUNTDOWN_VALUE: value},
blocking=True, blocking=True,

View File

@@ -4,7 +4,7 @@ from homeassistant.components.agent_dvr.const import DOMAIN, SERVER_URL
from homeassistant.const import CONF_HOST, CONF_PORT, CONTENT_TYPE_JSON from homeassistant.const import CONF_HOST, CONF_PORT, CONTENT_TYPE_JSON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry, load_fixture from tests.common import MockConfigEntry, async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
CONF_DATA = { CONF_DATA = {
@@ -34,12 +34,12 @@ async def init_integration(
aioclient_mock.get( aioclient_mock.get(
"http://example.local:8090/command.cgi?cmd=getStatus", "http://example.local:8090/command.cgi?cmd=getStatus",
text=load_fixture("agent_dvr/status.json"), text=await async_load_fixture(hass, "status.json", DOMAIN),
headers={"Content-Type": CONTENT_TYPE_JSON}, headers={"Content-Type": CONTENT_TYPE_JSON},
) )
aioclient_mock.get( aioclient_mock.get(
"http://example.local:8090/command.cgi?cmd=getObjects", "http://example.local:8090/command.cgi?cmd=getObjects",
text=load_fixture("agent_dvr/objects.json"), text=await async_load_fixture(hass, "objects.json", DOMAIN),
headers={"Content-Type": CONTENT_TYPE_JSON}, headers={"Content-Type": CONTENT_TYPE_JSON},
) )
entry = create_entry(hass) entry = create_entry(hass)

View File

@@ -2,8 +2,7 @@
import pytest import pytest
from homeassistant.components.agent_dvr import config_flow from homeassistant.components.agent_dvr.const import DOMAIN, SERVER_URL
from homeassistant.components.agent_dvr.const import SERVER_URL
from homeassistant.config_entries import SOURCE_USER from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_HOST, CONF_PORT, CONTENT_TYPE_JSON from homeassistant.const import CONF_HOST, CONF_PORT, CONTENT_TYPE_JSON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -11,7 +10,7 @@ from homeassistant.data_entry_flow import FlowResultType
from . import init_integration from . import init_integration
from tests.common import load_fixture from tests.common import async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
pytestmark = pytest.mark.usefixtures("mock_setup_entry") pytestmark = pytest.mark.usefixtures("mock_setup_entry")
@@ -20,7 +19,7 @@ pytestmark = pytest.mark.usefixtures("mock_setup_entry")
async def test_show_user_form(hass: HomeAssistant) -> None: async def test_show_user_form(hass: HomeAssistant) -> None:
"""Test that the user set up form is served.""" """Test that the user set up form is served."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, DOMAIN,
context={"source": SOURCE_USER}, context={"source": SOURCE_USER},
) )
@@ -35,7 +34,7 @@ async def test_user_device_exists_abort(
await init_integration(hass, aioclient_mock) await init_integration(hass, aioclient_mock)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, DOMAIN,
context={"source": SOURCE_USER}, context={"source": SOURCE_USER},
data={CONF_HOST: "example.local", CONF_PORT: 8090}, data={CONF_HOST: "example.local", CONF_PORT: 8090},
) )
@@ -51,7 +50,7 @@ async def test_connection_error(
aioclient_mock.get("http://example.local:8090/command.cgi?cmd=getStatus", text="") aioclient_mock.get("http://example.local:8090/command.cgi?cmd=getStatus", text="")
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, DOMAIN,
context={"source": SOURCE_USER}, context={"source": SOURCE_USER},
data={CONF_HOST: "example.local", CONF_PORT: 8090}, data={CONF_HOST: "example.local", CONF_PORT: 8090},
) )
@@ -67,18 +66,18 @@ async def test_full_user_flow_implementation(
"""Test the full manual user flow from start to finish.""" """Test the full manual user flow from start to finish."""
aioclient_mock.get( aioclient_mock.get(
"http://example.local:8090/command.cgi?cmd=getStatus", "http://example.local:8090/command.cgi?cmd=getStatus",
text=load_fixture("agent_dvr/status.json"), text=await async_load_fixture(hass, "status.json", DOMAIN),
headers={"Content-Type": CONTENT_TYPE_JSON}, headers={"Content-Type": CONTENT_TYPE_JSON},
) )
aioclient_mock.get( aioclient_mock.get(
"http://example.local:8090/command.cgi?cmd=getObjects", "http://example.local:8090/command.cgi?cmd=getObjects",
text=load_fixture("agent_dvr/objects.json"), text=await async_load_fixture(hass, "objects.json", DOMAIN),
headers={"Content-Type": CONTENT_TYPE_JSON}, headers={"Content-Type": CONTENT_TYPE_JSON},
) )
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, DOMAIN,
context={"source": SOURCE_USER}, context={"source": SOURCE_USER},
) )
@@ -95,5 +94,5 @@ async def test_full_user_flow_implementation(
assert result["title"] == "DESKTOP" assert result["title"] == "DESKTOP"
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
entries = hass.config_entries.async_entries(config_flow.DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert entries[0].unique_id == "c0715bba-c2d0-48ef-9e3e-bc81c9ea4447" assert entries[0].unique_id == "c0715bba-c2d0-48ef-9e3e-bc81c9ea4447"

View File

@@ -20,7 +20,7 @@ from . import setup_integration
from tests.common import ( from tests.common import (
MockConfigEntry, MockConfigEntry,
async_fire_time_changed, async_fire_time_changed,
load_fixture, async_load_fixture,
snapshot_platform, snapshot_platform,
) )
@@ -81,7 +81,7 @@ async def test_cloud_creates_no_button(
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
mock_cloud_airgradient_client.get_config.return_value = Config.from_json( mock_cloud_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_local.json", DOMAIN) await async_load_fixture(hass, "get_config_local.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=5)) freezer.tick(timedelta(minutes=5))
@@ -91,7 +91,7 @@ async def test_cloud_creates_no_button(
assert len(hass.states.async_all()) == 2 assert len(hass.states.async_all()) == 2
mock_cloud_airgradient_client.get_config.return_value = Config.from_json( mock_cloud_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_cloud.json", DOMAIN) await async_load_fixture(hass, "get_config_cloud.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=5)) freezer.tick(timedelta(minutes=5))

View File

@@ -24,7 +24,7 @@ from . import setup_integration
from tests.common import ( from tests.common import (
MockConfigEntry, MockConfigEntry,
async_fire_time_changed, async_fire_time_changed,
load_fixture, async_load_fixture,
snapshot_platform, snapshot_platform,
) )
@@ -83,7 +83,7 @@ async def test_cloud_creates_no_number(
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
mock_cloud_airgradient_client.get_config.return_value = Config.from_json( mock_cloud_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_local.json", DOMAIN) await async_load_fixture(hass, "get_config_local.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=5)) freezer.tick(timedelta(minutes=5))
@@ -93,7 +93,7 @@ async def test_cloud_creates_no_number(
assert len(hass.states.async_all()) == 2 assert len(hass.states.async_all()) == 2
mock_cloud_airgradient_client.get_config.return_value = Config.from_json( mock_cloud_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_cloud.json", DOMAIN) await async_load_fixture(hass, "get_config_cloud.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=5)) freezer.tick(timedelta(minutes=5))

View File

@@ -23,7 +23,7 @@ from . import setup_integration
from tests.common import ( from tests.common import (
MockConfigEntry, MockConfigEntry,
async_fire_time_changed, async_fire_time_changed,
load_fixture, async_load_fixture,
snapshot_platform, snapshot_platform,
) )
@@ -77,7 +77,7 @@ async def test_cloud_creates_no_number(
assert len(hass.states.async_all()) == 1 assert len(hass.states.async_all()) == 1
mock_cloud_airgradient_client.get_config.return_value = Config.from_json( mock_cloud_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_local.json", DOMAIN) await async_load_fixture(hass, "get_config_local.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=5)) freezer.tick(timedelta(minutes=5))
@@ -87,7 +87,7 @@ async def test_cloud_creates_no_number(
assert len(hass.states.async_all()) == 7 assert len(hass.states.async_all()) == 7
mock_cloud_airgradient_client.get_config.return_value = Config.from_json( mock_cloud_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_cloud.json", DOMAIN) await async_load_fixture(hass, "get_config_cloud.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=5)) freezer.tick(timedelta(minutes=5))

View File

@@ -18,7 +18,7 @@ from . import setup_integration
from tests.common import ( from tests.common import (
MockConfigEntry, MockConfigEntry,
async_fire_time_changed, async_fire_time_changed,
load_fixture, async_load_fixture,
snapshot_platform, snapshot_platform,
) )
@@ -46,14 +46,14 @@ async def test_create_entities(
) -> None: ) -> None:
"""Test creating entities.""" """Test creating entities."""
mock_airgradient_client.get_current_measures.return_value = Measures.from_json( mock_airgradient_client.get_current_measures.return_value = Measures.from_json(
load_fixture("measures_after_boot.json", DOMAIN) await async_load_fixture(hass, "measures_after_boot.json", DOMAIN)
) )
with patch("homeassistant.components.airgradient.PLATFORMS", [Platform.SENSOR]): with patch("homeassistant.components.airgradient.PLATFORMS", [Platform.SENSOR]):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
mock_airgradient_client.get_current_measures.return_value = Measures.from_json( mock_airgradient_client.get_current_measures.return_value = Measures.from_json(
load_fixture("current_measures_indoor.json", DOMAIN) await async_load_fixture(hass, "current_measures_indoor.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=1)) freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass) async_fire_time_changed(hass)

View File

@@ -25,7 +25,7 @@ from . import setup_integration
from tests.common import ( from tests.common import (
MockConfigEntry, MockConfigEntry,
async_fire_time_changed, async_fire_time_changed,
load_fixture, async_load_fixture,
snapshot_platform, snapshot_platform,
) )
@@ -83,7 +83,7 @@ async def test_cloud_creates_no_switch(
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
mock_cloud_airgradient_client.get_config.return_value = Config.from_json( mock_cloud_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_local.json", DOMAIN) await async_load_fixture(hass, "get_config_local.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=5)) freezer.tick(timedelta(minutes=5))
@@ -93,7 +93,7 @@ async def test_cloud_creates_no_switch(
assert len(hass.states.async_all()) == 1 assert len(hass.states.async_all()) == 1
mock_cloud_airgradient_client.get_config.return_value = Config.from_json( mock_cloud_airgradient_client.get_config.return_value = Config.from_json(
load_fixture("get_config_cloud.json", DOMAIN) await async_load_fixture(hass, "get_config_cloud.json", DOMAIN)
) )
freezer.tick(timedelta(minutes=5)) freezer.tick(timedelta(minutes=5))

View File

@@ -3,7 +3,7 @@
from homeassistant.components.airly.const import DOMAIN from homeassistant.components.airly.const import DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry, load_fixture from tests.common import MockConfigEntry, async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
API_NEAREST_URL = "https://airapi.airly.eu/v2/measurements/nearest?lat=123.000000&lng=456.000000&maxDistanceKM=5.000000" API_NEAREST_URL = "https://airapi.airly.eu/v2/measurements/nearest?lat=123.000000&lng=456.000000&maxDistanceKM=5.000000"
@@ -34,7 +34,9 @@ async def init_integration(
) )
aioclient_mock.get( aioclient_mock.get(
API_POINT_URL, text=load_fixture("valid_station.json", DOMAIN), headers=HEADERS API_POINT_URL,
text=await async_load_fixture(hass, "valid_station.json", DOMAIN),
headers=HEADERS,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id) await hass.config_entries.async_setup(entry.entry_id)

View File

@@ -12,7 +12,7 @@ from homeassistant.data_entry_flow import FlowResultType
from . import API_NEAREST_URL, API_POINT_URL from . import API_NEAREST_URL, API_POINT_URL
from tests.common import MockConfigEntry, load_fixture, patch from tests.common import MockConfigEntry, async_load_fixture, patch
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
CONFIG = { CONFIG = {
@@ -55,7 +55,9 @@ async def test_invalid_location(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None: ) -> None:
"""Test that errors are shown when location is invalid.""" """Test that errors are shown when location is invalid."""
aioclient_mock.get(API_POINT_URL, text=load_fixture("no_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
)
aioclient_mock.get( aioclient_mock.get(
API_NEAREST_URL, API_NEAREST_URL,
@@ -74,9 +76,13 @@ async def test_invalid_location_for_point_and_nearest(
) -> None: ) -> None:
"""Test an abort when the location is wrong for the point and nearest methods.""" """Test an abort when the location is wrong for the point and nearest methods."""
aioclient_mock.get(API_POINT_URL, text=load_fixture("no_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
)
aioclient_mock.get(API_NEAREST_URL, text=load_fixture("no_station.json", "airly")) aioclient_mock.get(
API_NEAREST_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
)
with patch("homeassistant.components.airly.async_setup_entry", return_value=True): with patch("homeassistant.components.airly.async_setup_entry", return_value=True):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@@ -91,7 +97,9 @@ async def test_duplicate_error(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None: ) -> None:
"""Test that errors are shown when duplicates are added.""" """Test that errors are shown when duplicates are added."""
aioclient_mock.get(API_POINT_URL, text=load_fixture("valid_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
MockConfigEntry(domain=DOMAIN, unique_id="123-456", data=CONFIG).add_to_hass(hass) MockConfigEntry(domain=DOMAIN, unique_id="123-456", data=CONFIG).add_to_hass(hass)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@@ -106,7 +114,9 @@ async def test_create_entry(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None: ) -> None:
"""Test that the user step works.""" """Test that the user step works."""
aioclient_mock.get(API_POINT_URL, text=load_fixture("valid_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
with patch("homeassistant.components.airly.async_setup_entry", return_value=True): with patch("homeassistant.components.airly.async_setup_entry", return_value=True):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@@ -126,10 +136,13 @@ async def test_create_entry_with_nearest_method(
) -> None: ) -> None:
"""Test that the user step works with nearest method.""" """Test that the user step works with nearest method."""
aioclient_mock.get(API_POINT_URL, text=load_fixture("no_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
)
aioclient_mock.get( aioclient_mock.get(
API_NEAREST_URL, text=load_fixture("valid_station.json", "airly") API_NEAREST_URL,
text=await async_load_fixture(hass, "valid_station.json", DOMAIN),
) )
with patch("homeassistant.components.airly.async_setup_entry", return_value=True): with patch("homeassistant.components.airly.async_setup_entry", return_value=True):

View File

@@ -15,7 +15,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import API_POINT_URL, init_integration from . import API_POINT_URL, init_integration
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture from tests.common import MockConfigEntry, async_fire_time_changed, async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
@@ -69,7 +69,9 @@ async def test_config_without_unique_id(
}, },
) )
aioclient_mock.get(API_POINT_URL, text=load_fixture("valid_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
entry.add_to_hass(hass) entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id) await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.LOADED assert entry.state is ConfigEntryState.LOADED
@@ -92,7 +94,9 @@ async def test_config_with_turned_off_station(
}, },
) )
aioclient_mock.get(API_POINT_URL, text=load_fixture("no_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
)
entry.add_to_hass(hass) entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id) await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.SETUP_RETRY assert entry.state is ConfigEntryState.SETUP_RETRY
@@ -124,7 +128,7 @@ async def test_update_interval(
aioclient_mock.get( aioclient_mock.get(
API_POINT_URL, API_POINT_URL,
text=load_fixture("valid_station.json", "airly"), text=await async_load_fixture(hass, "valid_station.json", DOMAIN),
headers=HEADERS, headers=HEADERS,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
@@ -159,7 +163,7 @@ async def test_update_interval(
aioclient_mock.get( aioclient_mock.get(
"https://airapi.airly.eu/v2/measurements/point?lat=66.660000&lng=111.110000", "https://airapi.airly.eu/v2/measurements/point?lat=66.660000&lng=111.110000",
text=load_fixture("valid_station.json", "airly"), text=await async_load_fixture(hass, "valid_station.json", DOMAIN),
headers=HEADERS, headers=HEADERS,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
@@ -216,7 +220,9 @@ async def test_migrate_device_entry(
}, },
) )
aioclient_mock.get(API_POINT_URL, text=load_fixture("valid_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
device_entry = device_registry.async_get_or_create( device_entry = device_registry.async_get_or_create(

View File

@@ -7,6 +7,7 @@ from unittest.mock import patch
from airly.exceptions import AirlyError from airly.exceptions import AirlyError
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.airly.const import DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
@@ -15,7 +16,7 @@ from homeassistant.util.dt import utcnow
from . import API_POINT_URL, init_integration from . import API_POINT_URL, init_integration
from tests.common import async_fire_time_changed, load_fixture from tests.common import async_fire_time_changed, async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
@@ -62,7 +63,9 @@ async def test_availability(
assert state.state == STATE_UNAVAILABLE assert state.state == STATE_UNAVAILABLE
aioclient_mock.clear_requests() aioclient_mock.clear_requests()
aioclient_mock.get(API_POINT_URL, text=load_fixture("valid_station.json", "airly")) aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
future = utcnow() + timedelta(minutes=120) future = utcnow() + timedelta(minutes=120)
async_fire_time_changed(hass, future) async_fire_time_changed(hass, future)
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@@ -1,8 +1,5 @@
"""The tests for Alarm control panel platforms.""" """The tests for Alarm control panel platforms."""
from homeassistant.components.alarm_control_panel import (
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -13,7 +10,7 @@ async def help_async_setup_entry_init(
) -> bool: ) -> bool:
"""Set up test config entry.""" """Set up test config entry."""
await hass.config_entries.async_forward_entry_setups( await hass.config_entries.async_forward_entry_setups(
config_entry, [ALARM_CONTROL_PANEL_DOMAIN] config_entry, [Platform.ALARM_CONTROL_PANEL]
) )
return True return True

View File

@@ -6,12 +6,13 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from homeassistant.components.alarm_control_panel import ( from homeassistant.components.alarm_control_panel import (
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN, DOMAIN,
AlarmControlPanelEntity, AlarmControlPanelEntity,
AlarmControlPanelEntityFeature, AlarmControlPanelEntityFeature,
) )
from homeassistant.components.alarm_control_panel.const import CodeFormat from homeassistant.components.alarm_control_panel.const import CodeFormat
from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er, frame from homeassistant.helpers import entity_registry as er, frame
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -172,7 +173,7 @@ async def setup_alarm_control_panel_platform_test_entity(
) -> bool: ) -> bool:
"""Set up test config entry.""" """Set up test config entry."""
await hass.config_entries.async_forward_entry_setups( await hass.config_entries.async_forward_entry_setups(
config_entry, [ALARM_CONTROL_PANEL_DOMAIN] config_entry, [Platform.ALARM_CONTROL_PANEL]
) )
return True return True
@@ -201,7 +202,7 @@ async def setup_alarm_control_panel_platform_test_entity(
mock_platform( mock_platform(
hass, hass,
f"{TEST_DOMAIN}.{ALARM_CONTROL_PANEL_DOMAIN}", f"{TEST_DOMAIN}.{DOMAIN}",
MockPlatform(async_setup_entry=async_setup_entry_platform), MockPlatform(async_setup_entry=async_setup_entry_platform),
) )

View File

@@ -6,7 +6,7 @@ import pytest
from homeassistant.components import alarm_control_panel from homeassistant.components import alarm_control_panel
from homeassistant.components.alarm_control_panel import ( from homeassistant.components.alarm_control_panel import (
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN, DOMAIN,
AlarmControlPanelEntityFeature, AlarmControlPanelEntityFeature,
CodeFormat, CodeFormat,
) )
@@ -280,9 +280,7 @@ async def test_alarm_control_panel_log_deprecated_state_warning_using_state_prop
), ),
built_in=False, built_in=False,
) )
setup_test_component_platform( setup_test_component_platform(hass, DOMAIN, [entity], from_config_entry=True)
hass, ALARM_CONTROL_PANEL_DOMAIN, [entity], from_config_entry=True
)
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
state = hass.states.get(entity.entity_id) state = hass.states.get(entity.entity_id)
@@ -343,9 +341,7 @@ async def test_alarm_control_panel_log_deprecated_state_warning_using_attr_state
), ),
built_in=False, built_in=False,
) )
setup_test_component_platform( setup_test_component_platform(hass, DOMAIN, [entity], from_config_entry=True)
hass, ALARM_CONTROL_PANEL_DOMAIN, [entity], from_config_entry=True
)
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
state = hass.states.get(entity.entity_id) state = hass.states.get(entity.entity_id)
@@ -426,9 +422,7 @@ async def test_alarm_control_panel_deprecated_state_does_not_break_state(
), ),
built_in=False, built_in=False,
) )
setup_test_component_platform( setup_test_component_platform(hass, DOMAIN, [entity], from_config_entry=True)
hass, ALARM_CONTROL_PANEL_DOMAIN, [entity], from_config_entry=True
)
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
state = hass.states.get(entity.entity_id) state = hass.states.get(entity.entity_id)

View File

@@ -16,6 +16,7 @@ from homeassistant.components.assist_pipeline.select import (
) )
from homeassistant.components.assist_pipeline.vad import VadSensitivity from homeassistant.components.assist_pipeline.vad import VadSensitivity
from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
@@ -53,7 +54,9 @@ async def init_select(hass: HomeAssistant, init_components) -> ConfigEntry:
domain="assist_pipeline", state=ConfigEntryState.LOADED domain="assist_pipeline", state=ConfigEntryState.LOADED
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
await hass.config_entries.async_forward_entry_setups(config_entry, ["select"]) await hass.config_entries.async_forward_entry_setups(
config_entry, [Platform.SELECT]
)
return config_entry return config_entry
@@ -160,8 +163,12 @@ async def test_select_entity_changing_pipelines(
assert state.state == pipeline_2.name assert state.state == pipeline_2.name
# Reload config entry to test selected option persists # Reload config entry to test selected option persists
assert await hass.config_entries.async_forward_entry_unload(config_entry, "select") assert await hass.config_entries.async_forward_entry_unload(
await hass.config_entries.async_forward_entry_setups(config_entry, ["select"]) config_entry, Platform.SELECT
)
await hass.config_entries.async_forward_entry_setups(
config_entry, [Platform.SELECT]
)
state = hass.states.get("select.assist_pipeline_test_prefix_pipeline") state = hass.states.get("select.assist_pipeline_test_prefix_pipeline")
assert state is not None assert state is not None
@@ -208,8 +215,12 @@ async def test_select_entity_changing_vad_sensitivity(
assert state.state == VadSensitivity.AGGRESSIVE.value assert state.state == VadSensitivity.AGGRESSIVE.value
# Reload config entry to test selected option persists # Reload config entry to test selected option persists
assert await hass.config_entries.async_forward_entry_unload(config_entry, "select") assert await hass.config_entries.async_forward_entry_unload(
await hass.config_entries.async_forward_entry_setups(config_entry, ["select"]) config_entry, Platform.SELECT
)
await hass.config_entries.async_forward_entry_setups(
config_entry, [Platform.SELECT]
)
state = hass.states.get("select.assist_pipeline_test_vad_sensitivity") state = hass.states.get("select.assist_pipeline_test_vad_sensitivity")
assert state is not None assert state is not None

View File

@@ -7,7 +7,7 @@ import pytest
from homeassistant.components.assist_pipeline import PipelineEvent from homeassistant.components.assist_pipeline import PipelineEvent
from homeassistant.components.assist_satellite import ( from homeassistant.components.assist_satellite import (
DOMAIN as AS_DOMAIN, DOMAIN,
AssistSatelliteAnnouncement, AssistSatelliteAnnouncement,
AssistSatelliteConfiguration, AssistSatelliteConfiguration,
AssistSatelliteEntity, AssistSatelliteEntity,
@@ -15,6 +15,7 @@ from homeassistant.components.assist_satellite import (
AssistSatelliteWakeWord, AssistSatelliteWakeWord,
) )
from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@@ -144,14 +145,18 @@ async def init_components(
hass: HomeAssistant, config_entry: ConfigEntry hass: HomeAssistant, config_entry: ConfigEntry
) -> bool: ) -> bool:
"""Set up test config entry.""" """Set up test config entry."""
await hass.config_entries.async_forward_entry_setups(config_entry, [AS_DOMAIN]) await hass.config_entries.async_forward_entry_setups(
config_entry, [Platform.ASSIST_SATELLITE]
)
return True return True
async def async_unload_entry_init( async def async_unload_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry hass: HomeAssistant, config_entry: ConfigEntry
) -> bool: ) -> bool:
"""Unload test config entry.""" """Unload test config entry."""
await hass.config_entries.async_forward_entry_unload(config_entry, AS_DOMAIN) await hass.config_entries.async_forward_entry_unload(
config_entry, Platform.ASSIST_SATELLITE
)
return True return True
mock_integration( mock_integration(
@@ -163,7 +168,7 @@ async def init_components(
), ),
) )
setup_test_component_platform( setup_test_component_platform(
hass, AS_DOMAIN, [entity, entity2, entity_no_features], from_config_entry=True hass, DOMAIN, [entity, entity2, entity_no_features], from_config_entry=True
) )
mock_platform(hass, f"{TEST_DOMAIN}.config_flow", Mock()) mock_platform(hass, f"{TEST_DOMAIN}.config_flow", Mock())

View File

@@ -3,10 +3,7 @@
from typing import Any from typing import Any
from unittest.mock import patch from unittest.mock import patch
from homeassistant.components.aussie_broadband.const import ( from homeassistant.components.aussie_broadband.const import CONF_SERVICES, DOMAIN
CONF_SERVICES,
DOMAIN as AUSSIE_BROADBAND_DOMAIN,
)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import UNDEFINED, UndefinedType from homeassistant.helpers.typing import UNDEFINED, UndefinedType
@@ -49,7 +46,7 @@ async def setup_platform(
): ):
"""Set up the Aussie Broadband platform.""" """Set up the Aussie Broadband platform."""
mock_entry = MockConfigEntry( mock_entry = MockConfigEntry(
domain=AUSSIE_BROADBAND_DOMAIN, domain=DOMAIN,
data=FAKE_DATA, data=FAKE_DATA,
options={ options={
CONF_SERVICES: ["12345678", "87654321", "23456789", "98765432"], CONF_SERVICES: ["12345678", "87654321", "23456789", "98765432"],

View File

@@ -12,7 +12,7 @@ from axis.rtsp import Signal, State
import pytest import pytest
import respx import respx
from homeassistant.components.axis.const import DOMAIN as AXIS_DOMAIN from homeassistant.components.axis.const import DOMAIN
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
CONF_MODEL, CONF_MODEL,
@@ -91,7 +91,7 @@ def fixture_config_entry(
) -> MockConfigEntry: ) -> MockConfigEntry:
"""Define a config entry fixture.""" """Define a config entry fixture."""
return MockConfigEntry( return MockConfigEntry(
domain=AXIS_DOMAIN, domain=DOMAIN,
entry_id="676abe5b73621446e6550a2e86ffe3dd", entry_id="676abe5b73621446e6550a2e86ffe3dd",
unique_id=FORMATTED_MAC, unique_id=FORMATTED_MAC,
data=config_entry_data, data=config_entry_data,

View File

@@ -12,7 +12,7 @@ from homeassistant.components.axis.const import (
CONF_VIDEO_SOURCE, CONF_VIDEO_SOURCE,
DEFAULT_STREAM_PROFILE, DEFAULT_STREAM_PROFILE,
DEFAULT_VIDEO_SOURCE, DEFAULT_VIDEO_SOURCE,
DOMAIN as AXIS_DOMAIN, DOMAIN,
) )
from homeassistant.config_entries import ( from homeassistant.config_entries import (
SOURCE_DHCP, SOURCE_DHCP,
@@ -47,7 +47,7 @@ DHCP_FORMATTED_MAC = dr.format_mac(MAC).replace(":", "")
async def test_flow_manual_configuration(hass: HomeAssistant) -> None: async def test_flow_manual_configuration(hass: HomeAssistant) -> None:
"""Test that config flow works.""" """Test that config flow works."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, context={"source": SOURCE_USER} DOMAIN, context={"source": SOURCE_USER}
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
@@ -86,7 +86,7 @@ async def test_manual_configuration_duplicate_fails(
assert config_entry_setup.data[CONF_HOST] == "1.2.3.4" assert config_entry_setup.data[CONF_HOST] == "1.2.3.4"
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, context={"source": SOURCE_USER} DOMAIN, context={"source": SOURCE_USER}
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
@@ -122,7 +122,7 @@ async def test_flow_fails_on_api(
) -> None: ) -> None:
"""Test that config flow fails on faulty credentials.""" """Test that config flow fails on faulty credentials."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, context={"source": SOURCE_USER} DOMAIN, context={"source": SOURCE_USER}
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
@@ -152,18 +152,18 @@ async def test_flow_create_entry_multiple_existing_entries_of_same_model(
) -> None: ) -> None:
"""Test that create entry can generate a name with other entries.""" """Test that create entry can generate a name with other entries."""
entry = MockConfigEntry( entry = MockConfigEntry(
domain=AXIS_DOMAIN, domain=DOMAIN,
data={CONF_NAME: "M1065-LW 0", CONF_MODEL: "M1065-LW"}, data={CONF_NAME: "M1065-LW 0", CONF_MODEL: "M1065-LW"},
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
entry2 = MockConfigEntry( entry2 = MockConfigEntry(
domain=AXIS_DOMAIN, domain=DOMAIN,
data={CONF_NAME: "M1065-LW 1", CONF_MODEL: "M1065-LW"}, data={CONF_NAME: "M1065-LW 1", CONF_MODEL: "M1065-LW"},
) )
entry2.add_to_hass(hass) entry2.add_to_hass(hass)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, context={"source": SOURCE_USER} DOMAIN, context={"source": SOURCE_USER}
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
@@ -337,7 +337,7 @@ async def test_discovery_flow(
) -> None: ) -> None:
"""Test the different discovery flows for new devices work.""" """Test the different discovery flows for new devices work."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, data=discovery_info, context={"source": source} DOMAIN, data=discovery_info, context={"source": source}
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
@@ -420,7 +420,7 @@ async def test_discovered_device_already_configured(
assert config_entry_setup.data[CONF_HOST] == DEFAULT_HOST assert config_entry_setup.data[CONF_HOST] == DEFAULT_HOST
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, data=discovery_info, context={"source": source} DOMAIN, data=discovery_info, context={"source": source}
) )
assert result["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
@@ -488,7 +488,7 @@ async def test_discovery_flow_updated_configuration(
mock_requests("2.3.4.5") mock_requests("2.3.4.5")
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, data=discovery_info, context={"source": source} DOMAIN, data=discovery_info, context={"source": source}
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@@ -546,7 +546,7 @@ async def test_discovery_flow_ignore_non_axis_device(
) -> None: ) -> None:
"""Test that discovery flow ignores devices with non Axis OUI.""" """Test that discovery flow ignores devices with non Axis OUI."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, data=discovery_info, context={"source": source} DOMAIN, data=discovery_info, context={"source": source}
) )
assert result["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
@@ -595,7 +595,7 @@ async def test_discovery_flow_ignore_link_local_address(
) -> None: ) -> None:
"""Test that discovery flow ignores devices with link local addresses.""" """Test that discovery flow ignores devices with link local addresses."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
AXIS_DOMAIN, data=discovery_info, context={"source": source} DOMAIN, data=discovery_info, context={"source": source}
) )
assert result["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT

View File

@@ -12,7 +12,7 @@ import pytest
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components import axis from homeassistant.components import axis
from homeassistant.components.axis.const import DOMAIN as AXIS_DOMAIN from homeassistant.components.axis.const import DOMAIN
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.config_entries import SOURCE_ZEROCONF, ConfigEntryState from homeassistant.config_entries import SOURCE_ZEROCONF, ConfigEntryState
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE
@@ -43,7 +43,7 @@ async def test_device_registry_entry(
) -> None: ) -> None:
"""Successful setup.""" """Successful setup."""
device_entry = device_registry.async_get_device( device_entry = device_registry.async_get_device(
identifiers={(AXIS_DOMAIN, config_entry_setup.unique_id)} identifiers={(DOMAIN, config_entry_setup.unique_id)}
) )
assert device_entry == snapshot assert device_entry == snapshot
@@ -93,7 +93,7 @@ async def test_update_address(
mock_requests("2.3.4.5") mock_requests("2.3.4.5")
await hass.config_entries.flow.async_init( await hass.config_entries.flow.async_init(
AXIS_DOMAIN, DOMAIN,
data=ZeroconfServiceInfo( data=ZeroconfServiceInfo(
ip_address=ip_address("2.3.4.5"), ip_address=ip_address("2.3.4.5"),
ip_addresses=[ip_address("2.3.4.5")], ip_addresses=[ip_address("2.3.4.5")],

View File

@@ -2,7 +2,7 @@
from unittest.mock import MagicMock from unittest.mock import MagicMock
from homeassistant.components.balboa.const import DOMAIN as BALBOA_DOMAIN from homeassistant.components.balboa.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -24,7 +24,7 @@ async def test_setup_entry(
async def test_setup_entry_fails(hass: HomeAssistant, client: MagicMock) -> None: async def test_setup_entry_fails(hass: HomeAssistant, client: MagicMock) -> None:
"""Validate that setup entry also configure the client.""" """Validate that setup entry also configure the client."""
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
domain=BALBOA_DOMAIN, domain=DOMAIN,
data={ data={
CONF_HOST: TEST_HOST, CONF_HOST: TEST_HOST,
}, },

View File

@@ -7,7 +7,7 @@ import pytest
from homeassistant.components import binary_sensor from homeassistant.components import binary_sensor
from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import STATE_OFF, STATE_ON, EntityCategory from homeassistant.const import STATE_OFF, STATE_ON, EntityCategory, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -62,7 +62,7 @@ async def test_name(hass: HomeAssistant) -> None:
) -> bool: ) -> bool:
"""Set up test config entry.""" """Set up test config entry."""
await hass.config_entries.async_forward_entry_setups( await hass.config_entries.async_forward_entry_setups(
config_entry, [binary_sensor.DOMAIN] config_entry, [Platform.BINARY_SENSOR]
) )
return True return True
@@ -142,7 +142,7 @@ async def test_entity_category_config_raises_error(
) -> bool: ) -> bool:
"""Set up test config entry.""" """Set up test config entry."""
await hass.config_entries.async_forward_entry_setups( await hass.config_entries.async_forward_entry_setups(
config_entry, [binary_sensor.DOMAIN] config_entry, [Platform.BINARY_SENSOR]
) )
return True return True

View File

@@ -6,11 +6,11 @@ from pathlib import Path
import pytest import pytest
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.blueprint import importer from homeassistant.components.blueprint import DOMAIN, importer
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from tests.common import load_fixture from tests.common import async_load_fixture, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
@@ -161,7 +161,7 @@ async def test_fetch_blueprint_from_github_gist_url(
"""Test fetching blueprint from url.""" """Test fetching blueprint from url."""
aioclient_mock.get( aioclient_mock.get(
"https://api.github.com/gists/e717ce85dd0d2f1bdcdfc884ea25a344", "https://api.github.com/gists/e717ce85dd0d2f1bdcdfc884ea25a344",
text=load_fixture("blueprint/github_gist.json"), text=await async_load_fixture(hass, "github_gist.json", DOMAIN),
) )
url = "https://gist.github.com/balloob/e717ce85dd0d2f1bdcdfc884ea25a344" url = "https://gist.github.com/balloob/e717ce85dd0d2f1bdcdfc884ea25a344"

View File

@@ -9,7 +9,7 @@ import pytest
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props from syrupy.filters import props
from homeassistant.components.bluesound import DOMAIN as BLUESOUND_DOMAIN from homeassistant.components.bluesound import DOMAIN
from homeassistant.components.bluesound.const import ATTR_MASTER from homeassistant.components.bluesound.const import ATTR_MASTER
from homeassistant.components.bluesound.media_player import ( from homeassistant.components.bluesound.media_player import (
SERVICE_CLEAR_TIMER, SERVICE_CLEAR_TIMER,
@@ -230,7 +230,7 @@ async def test_set_sleep_timer(
) -> None: ) -> None:
"""Test the set sleep timer action.""" """Test the set sleep timer action."""
await hass.services.async_call( await hass.services.async_call(
BLUESOUND_DOMAIN, DOMAIN,
SERVICE_SET_TIMER, SERVICE_SET_TIMER,
{ATTR_ENTITY_ID: "media_player.player_name1111"}, {ATTR_ENTITY_ID: "media_player.player_name1111"},
blocking=True, blocking=True,
@@ -247,7 +247,7 @@ async def test_clear_sleep_timer(
player_mocks.player_data.player.sleep_timer.side_effect = [15, 30, 45, 60, 90, 0] player_mocks.player_data.player.sleep_timer.side_effect = [15, 30, 45, 60, 90, 0]
await hass.services.async_call( await hass.services.async_call(
BLUESOUND_DOMAIN, DOMAIN,
SERVICE_CLEAR_TIMER, SERVICE_CLEAR_TIMER,
{ATTR_ENTITY_ID: "media_player.player_name1111"}, {ATTR_ENTITY_ID: "media_player.player_name1111"},
blocking=True, blocking=True,
@@ -262,7 +262,7 @@ async def test_join_cannot_join_to_self(
"""Test that joining to self is not allowed.""" """Test that joining to self is not allowed."""
with pytest.raises(ServiceValidationError, match="Cannot join player to itself"): with pytest.raises(ServiceValidationError, match="Cannot join player to itself"):
await hass.services.async_call( await hass.services.async_call(
BLUESOUND_DOMAIN, DOMAIN,
SERVICE_JOIN, SERVICE_JOIN,
{ {
ATTR_ENTITY_ID: "media_player.player_name1111", ATTR_ENTITY_ID: "media_player.player_name1111",
@@ -280,7 +280,7 @@ async def test_join(
) -> None: ) -> None:
"""Test the join action.""" """Test the join action."""
await hass.services.async_call( await hass.services.async_call(
BLUESOUND_DOMAIN, DOMAIN,
SERVICE_JOIN, SERVICE_JOIN,
{ {
ATTR_ENTITY_ID: "media_player.player_name1111", ATTR_ENTITY_ID: "media_player.player_name1111",
@@ -311,7 +311,7 @@ async def test_unjoin(
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(
BLUESOUND_DOMAIN, DOMAIN,
"unjoin", "unjoin",
{ATTR_ENTITY_ID: "media_player.player_name1111"}, {ATTR_ENTITY_ID: "media_player.player_name1111"},
blocking=True, blocking=True,

View File

@@ -41,7 +41,7 @@ from . import (
patch_bluetooth_time, patch_bluetooth_time,
) )
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture from tests.common import MockConfigEntry, async_fire_time_changed, async_load_fixture
@pytest.mark.parametrize("name_2", [None, "w"]) @pytest.mark.parametrize("name_2", [None, "w"])
@@ -313,7 +313,7 @@ async def test_restore_history_remote_adapter(
"""Test we can restore history for a remote adapter.""" """Test we can restore history for a remote adapter."""
data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads( data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads(
load_fixture("bluetooth.remote_scanners", bluetooth.DOMAIN) await async_load_fixture(hass, "bluetooth.remote_scanners", bluetooth.DOMAIN)
) )
now = time.time() now = time.time()
timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][ timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][

View File

@@ -63,7 +63,7 @@ from tests.common import (
MockModule, MockModule,
async_call_logger_set_level, async_call_logger_set_level,
async_fire_time_changed, async_fire_time_changed,
load_fixture, async_load_fixture,
mock_integration, mock_integration,
) )
@@ -453,7 +453,7 @@ async def test_restore_history_from_dbus_and_remote_adapters(
address = "AA:BB:CC:CC:CC:FF" address = "AA:BB:CC:CC:CC:FF"
data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads( data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads(
load_fixture("bluetooth.remote_scanners", bluetooth.DOMAIN) await async_load_fixture(hass, "bluetooth.remote_scanners", bluetooth.DOMAIN)
) )
now = time.time() now = time.time()
timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][ timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][
@@ -495,7 +495,9 @@ async def test_restore_history_from_dbus_and_corrupted_remote_adapters(
address = "AA:BB:CC:CC:CC:FF" address = "AA:BB:CC:CC:CC:FF"
data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads( data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads(
load_fixture("bluetooth.remote_scanners.corrupt", bluetooth.DOMAIN) await async_load_fixture(
hass, "bluetooth.remote_scanners.corrupt", bluetooth.DOMAIN
)
) )
now = time.time() now = time.time()
timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][ timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][

View File

@@ -13,7 +13,7 @@ from homeassistant.components.bmw_connected_drive.const import (
CONF_GCID, CONF_GCID,
CONF_READ_ONLY, CONF_READ_ONLY,
CONF_REFRESH_TOKEN, CONF_REFRESH_TOKEN,
DOMAIN as BMW_DOMAIN, DOMAIN,
) )
from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -34,7 +34,7 @@ FIXTURE_GCID = "DUMMY"
FIXTURE_CONFIG_ENTRY = { FIXTURE_CONFIG_ENTRY = {
"entry_id": "1", "entry_id": "1",
"domain": BMW_DOMAIN, "domain": DOMAIN,
"title": FIXTURE_USER_INPUT[CONF_USERNAME], "title": FIXTURE_USER_INPUT[CONF_USERNAME],
"data": { "data": {
CONF_USERNAME: FIXTURE_USER_INPUT[CONF_USERNAME], CONF_USERNAME: FIXTURE_USER_INPUT[CONF_USERNAME],

View File

@@ -11,7 +11,7 @@ from bimmer_connected.models import (
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from homeassistant.components.bmw_connected_drive import DOMAIN as BMW_DOMAIN from homeassistant.components.bmw_connected_drive import DOMAIN
from homeassistant.components.bmw_connected_drive.const import ( from homeassistant.components.bmw_connected_drive.const import (
CONF_REFRESH_TOKEN, CONF_REFRESH_TOKEN,
SCAN_INTERVALS, SCAN_INTERVALS,
@@ -140,7 +140,7 @@ async def test_auth_failed_as_update_failed(
# Verify that no issues are raised and no reauth flow is initialized # Verify that no issues are raised and no reauth flow is initialized
assert len(issue_registry.issues) == 0 assert len(issue_registry.issues) == 0
assert len(hass.config_entries.flow.async_progress_by_handler(BMW_DOMAIN)) == 0 assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 0
@pytest.mark.usefixtures("bmw_fixture") @pytest.mark.usefixtures("bmw_fixture")
@@ -190,13 +190,13 @@ async def test_auth_failed_init_reauth(
reauth_issue = issue_registry.async_get_issue( reauth_issue = issue_registry.async_get_issue(
HOMEASSISTANT_DOMAIN, HOMEASSISTANT_DOMAIN,
f"config_entry_reauth_{BMW_DOMAIN}_{config_entry.entry_id}", f"config_entry_reauth_{DOMAIN}_{config_entry.entry_id}",
) )
assert reauth_issue.active is True assert reauth_issue.active is True
# Check if reauth flow is initialized correctly # Check if reauth flow is initialized correctly
flow = hass.config_entries.flow.async_get(reauth_issue.data["flow_id"]) flow = hass.config_entries.flow.async_get(reauth_issue.data["flow_id"])
assert flow["handler"] == BMW_DOMAIN assert flow["handler"] == DOMAIN
assert flow["context"]["source"] == "reauth" assert flow["context"]["source"] == "reauth"
assert flow["context"]["unique_id"] == config_entry.unique_id assert flow["context"]["unique_id"] == config_entry.unique_id
@@ -233,12 +233,12 @@ async def test_captcha_reauth(
reauth_issue = issue_registry.async_get_issue( reauth_issue = issue_registry.async_get_issue(
HOMEASSISTANT_DOMAIN, HOMEASSISTANT_DOMAIN,
f"config_entry_reauth_{BMW_DOMAIN}_{config_entry.entry_id}", f"config_entry_reauth_{DOMAIN}_{config_entry.entry_id}",
) )
assert reauth_issue.active is True assert reauth_issue.active is True
# Check if reauth flow is initialized correctly # Check if reauth flow is initialized correctly
flow = hass.config_entries.flow.async_get(reauth_issue.data["flow_id"]) flow = hass.config_entries.flow.async_get(reauth_issue.data["flow_id"])
assert flow["handler"] == BMW_DOMAIN assert flow["handler"] == DOMAIN
assert flow["context"]["source"] == "reauth" assert flow["context"]["source"] == "reauth"
assert flow["context"]["unique_id"] == config_entry.unique_id assert flow["context"]["unique_id"] == config_entry.unique_id

View File

@@ -6,10 +6,7 @@ from unittest.mock import patch
import pytest import pytest
from homeassistant.components.bmw_connected_drive import DEFAULT_OPTIONS from homeassistant.components.bmw_connected_drive import DEFAULT_OPTIONS
from homeassistant.components.bmw_connected_drive.const import ( from homeassistant.components.bmw_connected_drive.const import CONF_READ_ONLY, DOMAIN
CONF_READ_ONLY,
DOMAIN as BMW_DOMAIN,
)
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
@@ -82,7 +79,7 @@ async def test_migrate_options_from_data(hass: HomeAssistant) -> None:
( (
{ {
"domain": SENSOR_DOMAIN, "domain": SENSOR_DOMAIN,
"platform": BMW_DOMAIN, "platform": DOMAIN,
"unique_id": f"{VIN}-charging_level_hv", "unique_id": f"{VIN}-charging_level_hv",
"suggested_object_id": f"{VEHICLE_NAME} charging_level_hv", "suggested_object_id": f"{VEHICLE_NAME} charging_level_hv",
"disabled_by": None, "disabled_by": None,
@@ -93,7 +90,7 @@ async def test_migrate_options_from_data(hass: HomeAssistant) -> None:
( (
{ {
"domain": SENSOR_DOMAIN, "domain": SENSOR_DOMAIN,
"platform": BMW_DOMAIN, "platform": DOMAIN,
"unique_id": f"{VIN}-remaining_range_total", "unique_id": f"{VIN}-remaining_range_total",
"suggested_object_id": f"{VEHICLE_NAME} remaining_range_total", "suggested_object_id": f"{VEHICLE_NAME} remaining_range_total",
"disabled_by": None, "disabled_by": None,
@@ -104,7 +101,7 @@ async def test_migrate_options_from_data(hass: HomeAssistant) -> None:
( (
{ {
"domain": SENSOR_DOMAIN, "domain": SENSOR_DOMAIN,
"platform": BMW_DOMAIN, "platform": DOMAIN,
"unique_id": f"{VIN}-mileage", "unique_id": f"{VIN}-mileage",
"suggested_object_id": f"{VEHICLE_NAME} mileage", "suggested_object_id": f"{VEHICLE_NAME} mileage",
"disabled_by": None, "disabled_by": None,
@@ -115,7 +112,7 @@ async def test_migrate_options_from_data(hass: HomeAssistant) -> None:
( (
{ {
"domain": SENSOR_DOMAIN, "domain": SENSOR_DOMAIN,
"platform": BMW_DOMAIN, "platform": DOMAIN,
"unique_id": f"{VIN}-charging_status", "unique_id": f"{VIN}-charging_status",
"suggested_object_id": f"{VEHICLE_NAME} Charging Status", "suggested_object_id": f"{VEHICLE_NAME} Charging Status",
"disabled_by": None, "disabled_by": None,
@@ -126,7 +123,7 @@ async def test_migrate_options_from_data(hass: HomeAssistant) -> None:
( (
{ {
"domain": BINARY_SENSOR_DOMAIN, "domain": BINARY_SENSOR_DOMAIN,
"platform": BMW_DOMAIN, "platform": DOMAIN,
"unique_id": f"{VIN}-charging_status", "unique_id": f"{VIN}-charging_status",
"suggested_object_id": f"{VEHICLE_NAME} Charging Status", "suggested_object_id": f"{VEHICLE_NAME} Charging Status",
"disabled_by": None, "disabled_by": None,
@@ -173,7 +170,7 @@ async def test_migrate_unique_ids(
( (
{ {
"domain": SENSOR_DOMAIN, "domain": SENSOR_DOMAIN,
"platform": BMW_DOMAIN, "platform": DOMAIN,
"unique_id": f"{VIN}-charging_level_hv", "unique_id": f"{VIN}-charging_level_hv",
"suggested_object_id": f"{VEHICLE_NAME} charging_level_hv", "suggested_object_id": f"{VEHICLE_NAME} charging_level_hv",
"disabled_by": None, "disabled_by": None,
@@ -198,7 +195,7 @@ async def test_dont_migrate_unique_ids(
# create existing entry with new_unique_id # create existing entry with new_unique_id
existing_entity = entity_registry.async_get_or_create( existing_entity = entity_registry.async_get_or_create(
SENSOR_DOMAIN, SENSOR_DOMAIN,
BMW_DOMAIN, DOMAIN,
unique_id=f"{VIN}-fuel_and_battery.remaining_battery_percent", unique_id=f"{VIN}-fuel_and_battery.remaining_battery_percent",
suggested_object_id=f"{VEHICLE_NAME} fuel_and_battery.remaining_battery_percent", suggested_object_id=f"{VEHICLE_NAME} fuel_and_battery.remaining_battery_percent",
config_entry=mock_config_entry, config_entry=mock_config_entry,
@@ -241,7 +238,7 @@ async def test_remove_stale_devices(
device_registry.async_get_or_create( device_registry.async_get_or_create(
config_entry_id=mock_config_entry.entry_id, config_entry_id=mock_config_entry.entry_id,
identifiers={(BMW_DOMAIN, "stale_device_id")}, identifiers={(DOMAIN, "stale_device_id")},
) )
device_entries = dr.async_entries_for_config_entry( device_entries = dr.async_entries_for_config_entry(
device_registry, mock_config_entry.entry_id device_registry, mock_config_entry.entry_id
@@ -249,7 +246,7 @@ async def test_remove_stale_devices(
assert len(device_entries) == 1 assert len(device_entries) == 1
device_entry = device_entries[0] device_entry = device_entries[0]
assert device_entry.identifiers == {(BMW_DOMAIN, "stale_device_id")} assert device_entry.identifiers == {(DOMAIN, "stale_device_id")}
assert await hass.config_entries.async_setup(mock_config_entry.entry_id) assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@@ -261,6 +258,4 @@ async def test_remove_stale_devices(
# Check that the test vehicles are still available but not the stale device # Check that the test vehicles are still available but not the stale device
assert len(device_entries) > 0 assert len(device_entries) > 0
remaining_device_identifiers = set().union(*(d.identifiers for d in device_entries)) remaining_device_identifiers = set().union(*(d.identifiers for d in device_entries))
assert not {(BMW_DOMAIN, "stale_device_id")}.intersection( assert not {(DOMAIN, "stale_device_id")}.intersection(remaining_device_identifiers)
remaining_device_identifiers
)

View File

@@ -8,7 +8,7 @@ import pytest
import respx import respx
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.bmw_connected_drive import DOMAIN as BMW_DOMAIN from homeassistant.components.bmw_connected_drive import DOMAIN
from homeassistant.components.bmw_connected_drive.select import SELECT_TYPES from homeassistant.components.bmw_connected_drive.select import SELECT_TYPES
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -182,9 +182,9 @@ async def test_entity_option_translations(
# Setup component to load translations # Setup component to load translations
assert await setup_mocked_integration(hass) assert await setup_mocked_integration(hass)
prefix = f"component.{BMW_DOMAIN}.entity.{Platform.SELECT.value}" prefix = f"component.{DOMAIN}.entity.{Platform.SELECT.value}"
translations = await async_get_translations(hass, "en", "entity", [BMW_DOMAIN]) translations = await async_get_translations(hass, "en", "entity", [DOMAIN])
translation_states = { translation_states = {
k for k in translations if k.startswith(prefix) and ".state." in k k for k in translations if k.startswith(prefix) and ".state." in k
} }

View File

@@ -8,7 +8,7 @@ from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.bmw_connected_drive import DOMAIN as BMW_DOMAIN from homeassistant.components.bmw_connected_drive import DOMAIN
from homeassistant.components.bmw_connected_drive.const import SCAN_INTERVALS from homeassistant.components.bmw_connected_drive.const import SCAN_INTERVALS
from homeassistant.components.bmw_connected_drive.sensor import SENSOR_TYPES from homeassistant.components.bmw_connected_drive.sensor import SENSOR_TYPES
from homeassistant.components.sensor import SensorDeviceClass from homeassistant.components.sensor import SensorDeviceClass
@@ -96,9 +96,9 @@ async def test_entity_option_translations(
# Setup component to load translations # Setup component to load translations
assert await setup_mocked_integration(hass) assert await setup_mocked_integration(hass)
prefix = f"component.{BMW_DOMAIN}.entity.{Platform.SENSOR.value}" prefix = f"component.{DOMAIN}.entity.{Platform.SENSOR.value}"
translations = await async_get_translations(hass, "en", "entity", [BMW_DOMAIN]) translations = await async_get_translations(hass, "en", "entity", [DOMAIN])
translation_states = { translation_states = {
k for k in translations if k.startswith(prefix) and ".state." in k k for k in translations if k.startswith(prefix) and ".state." in k
} }

View File

@@ -11,7 +11,7 @@ from aiohttp.client_exceptions import ClientResponseError
from bond_async import DeviceType from bond_async import DeviceType
from homeassistant import core from homeassistant import core
from homeassistant.components.bond.const import DOMAIN as BOND_DOMAIN from homeassistant.components.bond.const import DOMAIN
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, STATE_UNAVAILABLE from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, STATE_UNAVAILABLE
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.util import utcnow from homeassistant.util import utcnow
@@ -77,7 +77,7 @@ async def setup_platform(
): ):
"""Set up the specified Bond platform.""" """Set up the specified Bond platform."""
mock_entry = MockConfigEntry( mock_entry = MockConfigEntry(
domain=BOND_DOMAIN, domain=DOMAIN,
data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"}, data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
) )
mock_entry.add_to_hass(hass) mock_entry.add_to_hass(hass)
@@ -93,7 +93,7 @@ async def setup_platform(
patch_bond_device_properties(return_value=props), patch_bond_device_properties(return_value=props),
patch_bond_device_state(return_value=state), patch_bond_device_state(return_value=state),
): ):
assert await async_setup_component(hass, BOND_DOMAIN, {}) assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done() await hass.async_block_till_done()
return mock_entry return mock_entry

View File

@@ -11,7 +11,7 @@ import pytest
from homeassistant import core from homeassistant import core
from homeassistant.components import fan from homeassistant.components import fan
from homeassistant.components.bond.const import ( from homeassistant.components.bond.const import (
DOMAIN as BOND_DOMAIN, DOMAIN,
SERVICE_SET_FAN_SPEED_TRACKED_STATE, SERVICE_SET_FAN_SPEED_TRACKED_STATE,
) )
from homeassistant.components.bond.fan import PRESET_MODE_BREEZE from homeassistant.components.bond.fan import PRESET_MODE_BREEZE
@@ -367,7 +367,7 @@ async def test_set_speed_belief_speed_zero(hass: HomeAssistant) -> None:
with patch_bond_action() as mock_action, patch_bond_device_state(): with patch_bond_action() as mock_action, patch_bond_device_state():
await hass.services.async_call( await hass.services.async_call(
BOND_DOMAIN, DOMAIN,
SERVICE_SET_FAN_SPEED_TRACKED_STATE, SERVICE_SET_FAN_SPEED_TRACKED_STATE,
{ATTR_ENTITY_ID: "fan.name_1", "speed": 0}, {ATTR_ENTITY_ID: "fan.name_1", "speed": 0},
blocking=True, blocking=True,
@@ -391,7 +391,7 @@ async def test_set_speed_belief_speed_api_error(hass: HomeAssistant) -> None:
patch_bond_device_state(), patch_bond_device_state(),
): ):
await hass.services.async_call( await hass.services.async_call(
BOND_DOMAIN, DOMAIN,
SERVICE_SET_FAN_SPEED_TRACKED_STATE, SERVICE_SET_FAN_SPEED_TRACKED_STATE,
{ATTR_ENTITY_ID: "fan.name_1", "speed": 100}, {ATTR_ENTITY_ID: "fan.name_1", "speed": 100},
blocking=True, blocking=True,
@@ -406,7 +406,7 @@ async def test_set_speed_belief_speed_100(hass: HomeAssistant) -> None:
with patch_bond_action() as mock_action, patch_bond_device_state(): with patch_bond_action() as mock_action, patch_bond_device_state():
await hass.services.async_call( await hass.services.async_call(
BOND_DOMAIN, DOMAIN,
SERVICE_SET_FAN_SPEED_TRACKED_STATE, SERVICE_SET_FAN_SPEED_TRACKED_STATE,
{ATTR_ENTITY_ID: "fan.name_1", "speed": 100}, {ATTR_ENTITY_ID: "fan.name_1", "speed": 100},
blocking=True, blocking=True,

Some files were not shown because too many files have changed in this diff Show More