forked from home-assistant/core
Merge branch 'dev' into fix-microsign-alt1
This commit is contained in:
6
homeassistant/brands/shelly.json
Normal file
6
homeassistant/brands/shelly.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"domain": "shelly",
|
||||||
|
"name": "shelly",
|
||||||
|
"integrations": ["shelly"],
|
||||||
|
"iot_standards": ["zwave"]
|
||||||
|
}
|
@@ -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
|
||||||
|
36
homeassistant/components/airthings/coordinator.py
Normal file
36
homeassistant/components/airthings/coordinator.py
Normal 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
|
@@ -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:
|
||||||
|
@@ -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",
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -1 +1,3 @@
|
|||||||
"""The decora component."""
|
"""The decora component."""
|
||||||
|
|
||||||
|
DOMAIN = "decora"
|
||||||
|
@@ -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 = {}
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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."]
|
||||||
}
|
}
|
||||||
|
@@ -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."
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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}")
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
|
||||||
|
@@ -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,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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()
|
||||||
|
@@ -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__)
|
||||||
|
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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."]
|
||||||
}
|
}
|
||||||
|
@@ -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.
|
||||||
|
@@ -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(
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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"
|
||||||
]
|
]
|
||||||
|
@@ -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(
|
||||||
|
@@ -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"
|
||||||
},
|
},
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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"
|
||||||
},
|
},
|
||||||
|
@@ -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 = (
|
||||||
|
@@ -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,
|
||||||
|
@@ -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."
|
||||||
|
@@ -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
|
||||||
|
@@ -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):
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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] = {}
|
||||||
|
@@ -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")
|
||||||
|
@@ -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
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -1 +1,4 @@
|
|||||||
"""The tensorflow component."""
|
"""The tensorflow component."""
|
||||||
|
|
||||||
|
DOMAIN = "tensorflow"
|
||||||
|
CONF_GRAPH = "graph"
|
||||||
|
@@ -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(
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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,
|
||||||
),
|
),
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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"]
|
||||||
}
|
}
|
||||||
|
@@ -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:
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
|
|
||||||
|
@@ -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)},
|
||||||
|
@@ -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)."""
|
||||||
|
348
homeassistant/generated/dhcp.py
generated
348
homeassistant/generated/dhcp.py
generated
@@ -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",
|
||||||
|
@@ -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",
|
||||||
|
@@ -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(
|
||||||
|
@@ -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
|
||||||
|
@@ -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
6
requirements.txt
generated
@@ -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
34
requirements_all.txt
generated
@@ -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
|
||||||
|
53
requirements_test_all.txt
generated
53
requirements_test_all.txt
generated
@@ -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
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
2
script/hassfest/docker/Dockerfile
generated
2
script/hassfest/docker/Dockerfile
generated
@@ -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>"
|
||||||
|
@@ -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
|
||||||
|
@@ -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,
|
||||||
|
@@ -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,
|
||||||
|
@@ -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,
|
||||||
|
@@ -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,
|
||||||
|
@@ -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)
|
||||||
|
@@ -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"
|
||||||
|
@@ -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))
|
||||||
|
@@ -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))
|
||||||
|
@@ -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))
|
||||||
|
@@ -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)
|
||||||
|
@@ -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))
|
||||||
|
@@ -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)
|
||||||
|
@@ -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):
|
||||||
|
@@ -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(
|
||||||
|
@@ -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()
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
@@ -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())
|
||||||
|
|
||||||
|
@@ -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"],
|
||||||
|
@@ -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,
|
||||||
|
@@ -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
|
||||||
|
@@ -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")],
|
||||||
|
@@ -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,
|
||||||
},
|
},
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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"
|
||||||
|
@@ -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,
|
||||||
|
@@ -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"][
|
||||||
|
@@ -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"][
|
||||||
|
@@ -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],
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
|
||||||
)
|
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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
Reference in New Issue
Block a user