mirror of
https://github.com/home-assistant/core.git
synced 2025-08-08 07:05:07 +02:00
Merge branch 'dev' into dedupe_event_type
This commit is contained in:
@@ -395,7 +395,8 @@ omit =
|
||||
homeassistant/components/fritzbox_callmonitor/__init__.py
|
||||
homeassistant/components/fritzbox_callmonitor/base.py
|
||||
homeassistant/components/fritzbox_callmonitor/sensor.py
|
||||
homeassistant/components/frontier_silicon/const.py
|
||||
homeassistant/components/frontier_silicon/__init__.py
|
||||
homeassistant/components/frontier_silicon/browse_media.py
|
||||
homeassistant/components/frontier_silicon/media_player.py
|
||||
homeassistant/components/futurenow/light.py
|
||||
homeassistant/components/garadget/cover.py
|
||||
|
4
.github/workflows/builder.yml
vendored
4
.github/workflows/builder.yml
vendored
@@ -198,7 +198,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build base image
|
||||
uses: home-assistant/builder@2022.11.0
|
||||
uses: home-assistant/builder@2023.03.0
|
||||
with:
|
||||
args: |
|
||||
$BUILD_ARGS \
|
||||
@@ -276,7 +276,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build base image
|
||||
uses: home-assistant/builder@2022.11.0
|
||||
uses: home-assistant/builder@2023.03.0
|
||||
with:
|
||||
args: |
|
||||
$BUILD_ARGS \
|
||||
|
@@ -401,6 +401,7 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/frontend/ @home-assistant/frontend
|
||||
/tests/components/frontend/ @home-assistant/frontend
|
||||
/homeassistant/components/frontier_silicon/ @wlcrs
|
||||
/tests/components/frontier_silicon/ @wlcrs
|
||||
/homeassistant/components/fully_kiosk/ @cgarwood
|
||||
/tests/components/fully_kiosk/ @cgarwood
|
||||
/homeassistant/components/garages_amsterdam/ @klaasnicolaas
|
||||
|
@@ -1 +1,45 @@
|
||||
"""The frontier_silicon component."""
|
||||
"""The Frontier Silicon integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from afsapi import AFSAPI, ConnectionError as FSConnectionError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
|
||||
from .const import CONF_PIN, CONF_WEBFSAPI_URL, DOMAIN
|
||||
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Frontier Silicon from a config entry."""
|
||||
|
||||
webfsapi_url = entry.data[CONF_WEBFSAPI_URL]
|
||||
pin = entry.data[CONF_PIN]
|
||||
|
||||
afsapi = AFSAPI(webfsapi_url, pin)
|
||||
|
||||
try:
|
||||
await afsapi.get_power()
|
||||
except FSConnectionError as exception:
|
||||
raise PlatformNotReady from exception
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = afsapi
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
178
homeassistant/components/frontier_silicon/config_flow.py
Normal file
178
homeassistant/components/frontier_silicon/config_flow.py
Normal file
@@ -0,0 +1,178 @@
|
||||
"""Config flow for Frontier Silicon Media Player integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from afsapi import AFSAPI, ConnectionError as FSConnectionError, InvalidPinException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from .const import CONF_PIN, CONF_WEBFSAPI_URL, DEFAULT_PIN, DEFAULT_PORT, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): str,
|
||||
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_DEVICE_CONFIG_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_PIN,
|
||||
default=DEFAULT_PIN,
|
||||
): str,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Frontier Silicon Media Player."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize flow."""
|
||||
|
||||
self._webfsapi_url: str | None = None
|
||||
self._name: str | None = None
|
||||
self._unique_id: str | None = None
|
||||
|
||||
async def async_step_import(self, import_info: dict[str, Any]) -> FlowResult:
|
||||
"""Handle the import of legacy configuration.yaml entries."""
|
||||
|
||||
device_url = f"http://{import_info[CONF_HOST]}:{import_info[CONF_PORT]}/device"
|
||||
try:
|
||||
self._webfsapi_url = await AFSAPI.get_webfsapi_endpoint(device_url)
|
||||
except FSConnectionError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
except Exception as exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception(exception)
|
||||
return self.async_abort(reason="unknown")
|
||||
|
||||
try:
|
||||
afsapi = AFSAPI(self._webfsapi_url, import_info[CONF_PIN])
|
||||
|
||||
self._unique_id = await afsapi.get_radio_id()
|
||||
except FSConnectionError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
except InvalidPinException:
|
||||
return self.async_abort(reason="invalid_auth")
|
||||
except Exception as exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception(exception)
|
||||
return self.async_abort(reason="unknown")
|
||||
|
||||
await self.async_set_unique_id(self._unique_id, raise_on_progress=False)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
self._name = import_info[CONF_NAME] or "Radio"
|
||||
|
||||
return await self._create_entry(pin=import_info[CONF_PIN])
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step of manual configuration."""
|
||||
errors = {}
|
||||
|
||||
if user_input:
|
||||
device_url = (
|
||||
f"http://{user_input[CONF_HOST]}:{user_input[CONF_PORT]}/device"
|
||||
)
|
||||
try:
|
||||
self._webfsapi_url = await AFSAPI.get_webfsapi_endpoint(device_url)
|
||||
except FSConnectionError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except Exception as exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception(exception)
|
||||
errors["base"] = "unknown"
|
||||
else:
|
||||
return await self._async_step_device_config_if_needed()
|
||||
|
||||
data_schema = self.add_suggested_values_to_schema(
|
||||
STEP_USER_DATA_SCHEMA, user_input
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=data_schema, errors=errors
|
||||
)
|
||||
|
||||
async def _async_step_device_config_if_needed(self) -> FlowResult:
|
||||
"""Most users will not have changed the default PIN on their radio.
|
||||
|
||||
We try to use this default PIN, and only if this fails ask for it via `async_step_device_config`
|
||||
"""
|
||||
|
||||
try:
|
||||
# try to login with default pin
|
||||
afsapi = AFSAPI(self._webfsapi_url, DEFAULT_PIN)
|
||||
|
||||
self._name = await afsapi.get_friendly_name()
|
||||
except InvalidPinException:
|
||||
# Ask for a PIN
|
||||
return await self.async_step_device_config()
|
||||
|
||||
self.context["title_placeholders"] = {"name": self._name}
|
||||
|
||||
self._unique_id = await afsapi.get_radio_id()
|
||||
await self.async_set_unique_id(self._unique_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return await self._create_entry()
|
||||
|
||||
async def async_step_device_config(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle device configuration step.
|
||||
|
||||
We ask for the PIN in this step.
|
||||
"""
|
||||
assert self._webfsapi_url is not None
|
||||
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id="device_config", data_schema=STEP_DEVICE_CONFIG_DATA_SCHEMA
|
||||
)
|
||||
|
||||
errors = {}
|
||||
|
||||
try:
|
||||
afsapi = AFSAPI(self._webfsapi_url, user_input[CONF_PIN])
|
||||
|
||||
self._name = await afsapi.get_friendly_name()
|
||||
|
||||
except FSConnectionError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except InvalidPinException:
|
||||
errors["base"] = "invalid_auth"
|
||||
except Exception as exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception(exception)
|
||||
errors["base"] = "unknown"
|
||||
else:
|
||||
self._unique_id = await afsapi.get_radio_id()
|
||||
await self.async_set_unique_id(self._unique_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
return await self._create_entry(pin=user_input[CONF_PIN])
|
||||
|
||||
data_schema = self.add_suggested_values_to_schema(
|
||||
STEP_DEVICE_CONFIG_DATA_SCHEMA, user_input
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="device_config",
|
||||
data_schema=data_schema,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def _create_entry(self, pin: str | None = None) -> FlowResult:
|
||||
"""Create the entry."""
|
||||
assert self._name is not None
|
||||
assert self._webfsapi_url is not None
|
||||
|
||||
data = {CONF_WEBFSAPI_URL: self._webfsapi_url, CONF_PIN: pin or DEFAULT_PIN}
|
||||
|
||||
return self.async_create_entry(title=self._name, data=data)
|
@@ -1,6 +1,9 @@
|
||||
"""Constants for the Frontier Silicon Media Player integration."""
|
||||
DOMAIN = "frontier_silicon"
|
||||
|
||||
CONF_WEBFSAPI_URL = "webfsapi_url"
|
||||
CONF_PIN = "pin"
|
||||
|
||||
DEFAULT_PIN = "1234"
|
||||
DEFAULT_PORT = 80
|
||||
|
||||
|
@@ -2,6 +2,7 @@
|
||||
"domain": "frontier_silicon",
|
||||
"name": "Frontier Silicon",
|
||||
"codeowners": ["@wlcrs"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontier_silicon",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["afsapi==0.2.7"]
|
||||
|
@@ -21,15 +21,17 @@ from homeassistant.components.media_player import (
|
||||
MediaPlayerState,
|
||||
MediaType,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .browse_media import browse_node, browse_top_level
|
||||
from .const import DEFAULT_PIN, DEFAULT_PORT, DOMAIN, MEDIA_CONTENT_ID_PRESET
|
||||
from .const import CONF_PIN, DEFAULT_PIN, DEFAULT_PORT, DOMAIN, MEDIA_CONTENT_ID_PRESET
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -49,7 +51,11 @@ async def async_setup_platform(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Frontier Silicon platform."""
|
||||
"""Set up the Frontier Silicon platform.
|
||||
|
||||
YAML is deprecated, and imported automatically.
|
||||
SSDP discovery is temporarily retained - to be refactor subsequently.
|
||||
"""
|
||||
if discovery_info is not None:
|
||||
webfsapi_url = await AFSAPI.get_webfsapi_endpoint(
|
||||
discovery_info["ssdp_description"]
|
||||
@@ -61,24 +67,41 @@ async def async_setup_platform(
|
||||
[AFSAPIDevice(name, afsapi)],
|
||||
True,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
host = config.get(CONF_HOST)
|
||||
port = config.get(CONF_PORT)
|
||||
password = config.get(CONF_PASSWORD)
|
||||
name = config.get(CONF_NAME)
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"remove_yaml",
|
||||
breaks_in_ha_version="2023.6.0",
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="removed_yaml",
|
||||
)
|
||||
|
||||
try:
|
||||
webfsapi_url = await AFSAPI.get_webfsapi_endpoint(
|
||||
f"http://{host}:{port}/device"
|
||||
await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_NAME: config.get(CONF_NAME),
|
||||
CONF_HOST: config.get(CONF_HOST),
|
||||
CONF_PORT: config.get(CONF_PORT, DEFAULT_PORT),
|
||||
CONF_PIN: config.get(CONF_PASSWORD, DEFAULT_PIN),
|
||||
},
|
||||
)
|
||||
except FSConnectionError:
|
||||
_LOGGER.error(
|
||||
"Could not add the FSAPI device at %s:%s -> %s", host, port, password
|
||||
)
|
||||
return
|
||||
afsapi = AFSAPI(webfsapi_url, password)
|
||||
async_add_entities([AFSAPIDevice(name, afsapi)], True)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Frontier Silicon entity."""
|
||||
|
||||
afsapi: AFSAPI = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
async_add_entities([AFSAPIDevice(config_entry.title, afsapi)], True)
|
||||
|
||||
|
||||
class AFSAPIDevice(MediaPlayerEntity):
|
||||
|
35
homeassistant/components/frontier_silicon/strings.json
Normal file
35
homeassistant/components/frontier_silicon/strings.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Frontier Silicon Setup",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
}
|
||||
},
|
||||
"device_config": {
|
||||
"title": "Device Configuration",
|
||||
"description": "The pin can be found via 'MENU button > Main Menu > System setting > Network > NetRemote PIN setup'",
|
||||
"data": {
|
||||
"pin": "[%key:common::config_flow::data::pin%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"removed_yaml": {
|
||||
"title": "The Frontier Silicon YAML configuration has been removed",
|
||||
"description": "Configuring Frontier Silicon using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,19 +1,17 @@
|
||||
"""The Landis+Gyr Heat Meter integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import ultraheat_api
|
||||
from ultraheat_api.response import HeatMeterResponse
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_DEVICE, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_registry import async_migrate_entries
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import UltraheatCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -27,19 +25,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
reader = ultraheat_api.UltraheatReader(entry.data[CONF_DEVICE])
|
||||
api = ultraheat_api.HeatMeterService(reader)
|
||||
|
||||
async def async_update_data() -> HeatMeterResponse:
|
||||
"""Fetch data from the API."""
|
||||
_LOGGER.debug("Polling on %s", entry.data[CONF_DEVICE])
|
||||
return await hass.async_add_executor_job(api.read)
|
||||
|
||||
# Polling is only daily to prevent battery drain.
|
||||
coordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name="ultraheat_gateway",
|
||||
update_method=async_update_data,
|
||||
update_interval=timedelta(days=1),
|
||||
)
|
||||
coordinator = UltraheatCoordinator(hass, api)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
|
||||
|
@@ -1,6 +1,9 @@
|
||||
"""Constants for the Landis+Gyr Heat Meter integration."""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
DOMAIN = "landisgyr_heat_meter"
|
||||
|
||||
GJ_TO_MWH = 0.277778 # conversion factor
|
||||
ULTRAHEAT_TIMEOUT = 30 # reading the IR port can take some time
|
||||
POLLING_INTERVAL = timedelta(days=1) # Polling is only daily to prevent battery drain.
|
||||
|
37
homeassistant/components/landisgyr_heat_meter/coordinator.py
Normal file
37
homeassistant/components/landisgyr_heat_meter/coordinator.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""Data update coordinator for the ultraheat api."""
|
||||
|
||||
import logging
|
||||
|
||||
import async_timeout
|
||||
import serial
|
||||
from ultraheat_api.response import HeatMeterResponse
|
||||
from ultraheat_api.service import HeatMeterService
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import POLLING_INTERVAL, ULTRAHEAT_TIMEOUT
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UltraheatCoordinator(DataUpdateCoordinator[HeatMeterResponse]):
|
||||
"""Coordinator for getting data from the ultraheat api."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, api: HeatMeterService) -> None:
|
||||
"""Initialize my coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name="ultraheat",
|
||||
update_interval=POLLING_INTERVAL,
|
||||
)
|
||||
self.api = api
|
||||
|
||||
async def _async_update_data(self) -> HeatMeterResponse:
|
||||
"""Fetch data from API endpoint."""
|
||||
try:
|
||||
async with async_timeout.timeout(ULTRAHEAT_TIMEOUT):
|
||||
return await self.hass.async_add_executor_job(self.api.read)
|
||||
except (FileNotFoundError, serial.serialutil.SerialException) as err:
|
||||
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
@@ -1112,7 +1112,7 @@ def _migrate_columns_to_timestamp(
|
||||
result = session.connection().execute(
|
||||
text(
|
||||
"UPDATE events set time_fired_ts="
|
||||
"IF(time_fired is NULL,0,"
|
||||
"IF(time_fired is NULL or UNIX_TIMESTAMP(time_fired) is NULL,0,"
|
||||
"UNIX_TIMESTAMP(time_fired)"
|
||||
") "
|
||||
"where time_fired_ts is NULL "
|
||||
@@ -1125,7 +1125,7 @@ def _migrate_columns_to_timestamp(
|
||||
result = session.connection().execute(
|
||||
text(
|
||||
"UPDATE states set last_updated_ts="
|
||||
"IF(last_updated is NULL,0,"
|
||||
"IF(last_updated is NULL or UNIX_TIMESTAMP(last_updated) is NULL,0,"
|
||||
"UNIX_TIMESTAMP(last_updated) "
|
||||
"), "
|
||||
"last_changed_ts="
|
||||
@@ -1201,7 +1201,7 @@ def _migrate_statistics_columns_to_timestamp(
|
||||
result = session.connection().execute(
|
||||
text(
|
||||
f"UPDATE {table} set start_ts="
|
||||
"IF(start is NULL,0,"
|
||||
"IF(start is NULL or UNIX_TIMESTAMP(start) is NULL,0,"
|
||||
"UNIX_TIMESTAMP(start) "
|
||||
"), "
|
||||
"created_ts="
|
||||
|
@@ -27,8 +27,14 @@ async def async_get_config_entry_diagnostics(
|
||||
},
|
||||
"data": {
|
||||
"dsl": async_redact_data(dataclasses.asdict(data.dsl.data), TO_REDACT),
|
||||
"ftth": async_redact_data(
|
||||
dataclasses.asdict(await data.system.box.ftth_get_info()), TO_REDACT
|
||||
),
|
||||
"system": async_redact_data(
|
||||
dataclasses.asdict(data.system.data), TO_REDACT
|
||||
),
|
||||
"wan": async_redact_data(
|
||||
dataclasses.asdict(await data.system.box.wan_get_info()), TO_REDACT
|
||||
),
|
||||
},
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import dataclasses
|
||||
import logging
|
||||
from typing import cast
|
||||
|
||||
from python_otbr_api.mdns import StateBitmap
|
||||
from zeroconf import BadTypeInNameException, DNSPointer, ServiceListener, Zeroconf
|
||||
from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf
|
||||
|
||||
@@ -29,14 +30,15 @@ TYPE_PTR = 12
|
||||
class ThreadRouterDiscoveryData:
|
||||
"""Thread router discovery data."""
|
||||
|
||||
addresses: list[str] | None
|
||||
brand: str | None
|
||||
extended_pan_id: str | None
|
||||
model_name: str | None
|
||||
network_name: str | None
|
||||
server: str | None
|
||||
vendor_name: str | None
|
||||
addresses: list[str] | None
|
||||
thread_version: str | None
|
||||
unconfigured: bool | None
|
||||
vendor_name: str | None
|
||||
|
||||
|
||||
def async_discovery_data_from_service(
|
||||
@@ -59,15 +61,30 @@ def async_discovery_data_from_service(
|
||||
server = service.server
|
||||
vendor_name = try_decode(service.properties.get(b"vn"))
|
||||
thread_version = try_decode(service.properties.get(b"tv"))
|
||||
unconfigured = None
|
||||
brand = KNOWN_BRANDS.get(vendor_name)
|
||||
if brand == "homeassistant":
|
||||
# Attempt to detect incomplete configuration
|
||||
if (state_bitmap_b := service.properties.get(b"sb")) is not None:
|
||||
try:
|
||||
state_bitmap = StateBitmap.from_bytes(state_bitmap_b)
|
||||
if not state_bitmap.is_active:
|
||||
unconfigured = True
|
||||
except ValueError:
|
||||
_LOGGER.debug("Failed to decode state bitmap in service %s", service)
|
||||
if service.properties.get(b"at") is None:
|
||||
unconfigured = True
|
||||
|
||||
return ThreadRouterDiscoveryData(
|
||||
brand=KNOWN_BRANDS.get(vendor_name),
|
||||
addresses=service.parsed_addresses(),
|
||||
brand=brand,
|
||||
extended_pan_id=ext_pan_id.hex() if ext_pan_id is not None else None,
|
||||
model_name=model_name,
|
||||
network_name=network_name,
|
||||
server=server,
|
||||
vendor_name=vendor_name,
|
||||
addresses=service.parsed_addresses(),
|
||||
thread_version=thread_version,
|
||||
unconfigured=unconfigured,
|
||||
vendor_name=vendor_name,
|
||||
)
|
||||
|
||||
|
||||
|
@@ -213,3 +213,4 @@ class XiaomiPlugConsumerConnected(BinarySensor, id_suffix="consumer_connected"):
|
||||
|
||||
SENSOR_ATTR = "consumer_connected"
|
||||
_attr_name: str = "Consumer connected"
|
||||
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.PLUG
|
||||
|
@@ -145,6 +145,7 @@ FLOWS = {
|
||||
"fritzbox",
|
||||
"fritzbox_callmonitor",
|
||||
"fronius",
|
||||
"frontier_silicon",
|
||||
"fully_kiosk",
|
||||
"garages_amsterdam",
|
||||
"gdacs",
|
||||
|
@@ -1818,7 +1818,7 @@
|
||||
"frontier_silicon": {
|
||||
"name": "Frontier Silicon",
|
||||
"integration_type": "hub",
|
||||
"config_flow": false,
|
||||
"config_flow": true,
|
||||
"iot_class": "local_polling"
|
||||
},
|
||||
"fully_kiosk": {
|
||||
|
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Any, TypeVar, cast
|
||||
import attr
|
||||
|
||||
from homeassistant.backports.enum import StrEnum
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError, RequiredParameterMissing
|
||||
from homeassistant.loader import bind_hass
|
||||
@@ -907,6 +907,13 @@ def async_setup_cleanup(hass: HomeAssistant, dev_reg: DeviceRegistry) -> None:
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, startup_clean)
|
||||
|
||||
@callback
|
||||
def _on_homeassistant_stop(event: Event) -> None:
|
||||
"""Cancel debounced cleanup."""
|
||||
debounced_cleanup.async_cancel()
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _on_homeassistant_stop)
|
||||
|
||||
|
||||
def _normalize_connections(connections: set[tuple[str, str]]) -> set[tuple[str, str]]:
|
||||
"""Normalize connections to ensure we can match mac addresses."""
|
||||
|
@@ -24,11 +24,11 @@ pytest-cov==3.0.0
|
||||
pytest-freezer==0.4.6
|
||||
pytest-socket==0.5.1
|
||||
pytest-test-groups==1.0.3
|
||||
pytest-sugar==0.9.5
|
||||
pytest-sugar==0.9.6
|
||||
pytest-timeout==2.1.0
|
||||
pytest-unordered==0.5.2
|
||||
pytest-picked==0.4.6
|
||||
pytest-xdist==2.5.0
|
||||
pytest-xdist==3.2.0
|
||||
pytest==7.2.2
|
||||
requests_mock==1.10.0
|
||||
respx==0.20.1
|
||||
|
@@ -78,6 +78,9 @@ adguardhome==0.6.1
|
||||
# homeassistant.components.advantage_air
|
||||
advantage_air==0.4.1
|
||||
|
||||
# homeassistant.components.frontier_silicon
|
||||
afsapi==0.2.7
|
||||
|
||||
# homeassistant.components.agent_dvr
|
||||
agent-py==0.0.23
|
||||
|
||||
|
@@ -33,12 +33,12 @@ async def test_form(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) ->
|
||||
result1["flow_id"],
|
||||
USER_INPUT,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(aioclient_mock.mock_calls) == 1
|
||||
assert result2["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "testname"
|
||||
assert result2["data"] == USER_INPUT
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
# Test Duplicate Config Flow
|
||||
|
@@ -187,11 +187,11 @@ async def test_user_form_one_entry_per_device_allowed(hass: HomeAssistant) -> No
|
||||
result["flow_id"],
|
||||
{CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "already_configured"
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 0
|
||||
|
||||
|
||||
|
@@ -86,7 +86,7 @@ async def test_name(thermostat) -> None:
|
||||
assert thermostat.name == "Ecobee"
|
||||
|
||||
|
||||
async def test_aux_heat_not_supported_by_default(hass):
|
||||
async def test_aux_heat_not_supported_by_default(hass: HomeAssistant) -> None:
|
||||
"""Default setup should not support Aux heat."""
|
||||
await setup_platform(hass, const.Platform.CLIMATE)
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
@@ -100,7 +100,7 @@ async def test_aux_heat_not_supported_by_default(hass):
|
||||
)
|
||||
|
||||
|
||||
async def test_aux_heat_supported_with_heat_pump(hass):
|
||||
async def test_aux_heat_supported_with_heat_pump(hass: HomeAssistant) -> None:
|
||||
"""Aux Heat should be supported if thermostat has heatpump."""
|
||||
mock_get_thermostat = mock.Mock()
|
||||
mock_get_thermostat.return_value = GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP
|
||||
@@ -242,7 +242,7 @@ async def test_extra_state_attributes(ecobee_fixture, thermostat) -> None:
|
||||
} == thermostat.extra_state_attributes
|
||||
|
||||
|
||||
async def test_is_aux_heat_on(hass):
|
||||
async def test_is_aux_heat_on(hass: HomeAssistant) -> None:
|
||||
"""Test aux heat property is only enabled for auxHeatOnly."""
|
||||
mock_get_thermostat = mock.Mock()
|
||||
mock_get_thermostat.return_value = copy.deepcopy(
|
||||
@@ -255,7 +255,7 @@ async def test_is_aux_heat_on(hass):
|
||||
assert state.attributes[climate.ATTR_AUX_HEAT] == "on"
|
||||
|
||||
|
||||
async def test_is_aux_heat_off(hass):
|
||||
async def test_is_aux_heat_off(hass: HomeAssistant) -> None:
|
||||
"""Test aux heat property is only enabled for auxHeatOnly."""
|
||||
mock_get_thermostat = mock.Mock()
|
||||
mock_get_thermostat.return_value = GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP
|
||||
|
@@ -1,5 +1,4 @@
|
||||
"""Tests for the sensors provided by the EnergyZero integration."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from energyzero import EnergyZeroNoDataError
|
||||
|
@@ -33,11 +33,11 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
result["flow_id"],
|
||||
{CONF_HOST: "1.1.1.1", CONF_NAME: "test-epson"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == "test-epson"
|
||||
assert result2["data"] == {CONF_HOST: "1.1.1.1"}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
@@ -37,13 +37,13 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
"id": "test",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == "Test airport"
|
||||
assert result2["data"] == {
|
||||
"id": "test",
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
1
tests/components/frontier_silicon/__init__.py
Normal file
1
tests/components/frontier_silicon/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Tests for the Frontier Silicon integration."""
|
59
tests/components/frontier_silicon/conftest.py
Normal file
59
tests/components/frontier_silicon/conftest.py
Normal file
@@ -0,0 +1,59 @@
|
||||
"""Configuration for frontier_silicon tests."""
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.frontier_silicon.const import (
|
||||
CONF_PIN,
|
||||
CONF_WEBFSAPI_URL,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry() -> MockConfigEntry:
|
||||
"""Create a mock Frontier Silicon config entry."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="mock_radio_id",
|
||||
data={CONF_WEBFSAPI_URL: "http://1.1.1.1:80/webfsapi", CONF_PIN: "1234"},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_valid_device_url() -> Generator[None, None, None]:
|
||||
"""Return a valid webfsapi endpoint."""
|
||||
with patch(
|
||||
"afsapi.AFSAPI.get_webfsapi_endpoint",
|
||||
return_value="http://1.1.1.1:80/webfsapi",
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_valid_pin() -> Generator[None, None, None]:
|
||||
"""Make get_friendly_name return a value, indicating a valid pin."""
|
||||
with patch(
|
||||
"afsapi.AFSAPI.get_friendly_name",
|
||||
return_value="Name of the device",
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_radio_id() -> Generator[None, None, None]:
|
||||
"""Return a valid radio_id."""
|
||||
with patch("afsapi.AFSAPI.get_radio_id", return_value="mock_radio_id"):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.frontier_silicon.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
266
tests/components/frontier_silicon/test_config_flow.py
Normal file
266
tests/components/frontier_silicon/test_config_flow.py
Normal file
@@ -0,0 +1,266 @@
|
||||
"""Test the Frontier Silicon config flow."""
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from afsapi import ConnectionError, InvalidPinException
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.frontier_silicon.const import CONF_WEBFSAPI_URL, DOMAIN
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PIN, CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
||||
|
||||
async def test_import_success(hass: HomeAssistant) -> None:
|
||||
"""Test successful import."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: 80,
|
||||
CONF_PIN: "1234",
|
||||
CONF_NAME: "Test name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Test name"
|
||||
assert result["data"] == {
|
||||
CONF_WEBFSAPI_URL: "http://1.1.1.1:80/webfsapi",
|
||||
CONF_PIN: "1234",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("webfsapi_endpoint_error", "result_reason"),
|
||||
[
|
||||
(ConnectionError, "cannot_connect"),
|
||||
(ValueError, "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_import_webfsapi_endpoint_failures(
|
||||
hass: HomeAssistant, webfsapi_endpoint_error: Exception, result_reason: str
|
||||
) -> None:
|
||||
"""Test various failure of get_webfsapi_endpoint."""
|
||||
with patch(
|
||||
"afsapi.AFSAPI.get_webfsapi_endpoint",
|
||||
side_effect=webfsapi_endpoint_error,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: 80,
|
||||
CONF_PIN: "1234",
|
||||
CONF_NAME: "Test name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == result_reason
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("radio_id_error", "result_reason"),
|
||||
[
|
||||
(ConnectionError, "cannot_connect"),
|
||||
(InvalidPinException, "invalid_auth"),
|
||||
(ValueError, "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_import_radio_id_failures(
|
||||
hass: HomeAssistant, radio_id_error: Exception, result_reason: str
|
||||
) -> None:
|
||||
"""Test various failure of get_radio_id."""
|
||||
with patch(
|
||||
"afsapi.AFSAPI.get_radio_id",
|
||||
side_effect=radio_id_error,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: 80,
|
||||
CONF_PIN: "1234",
|
||||
CONF_NAME: "Test name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == result_reason
|
||||
|
||||
|
||||
async def test_import_already_exists(
|
||||
hass: HomeAssistant, config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test import of device which already exists."""
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: 80,
|
||||
CONF_PIN: "1234",
|
||||
CONF_NAME: "Test name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_form_default_pin(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Test manual device add with default pin."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "Name of the device"
|
||||
assert result2["data"] == {
|
||||
CONF_WEBFSAPI_URL: "http://1.1.1.1:80/webfsapi",
|
||||
CONF_PIN: "1234",
|
||||
}
|
||||
mock_setup_entry.assert_called_once()
|
||||
|
||||
|
||||
async def test_form_nondefault_pin(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"afsapi.AFSAPI.get_friendly_name",
|
||||
side_effect=InvalidPinException,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == FlowResultType.FORM
|
||||
assert result2["step_id"] == "device_config"
|
||||
assert result2["errors"] is None
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{CONF_PIN: "4321"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result3["title"] == "Name of the device"
|
||||
assert result3["data"] == {
|
||||
"webfsapi_url": "http://1.1.1.1:80/webfsapi",
|
||||
"pin": "4321",
|
||||
}
|
||||
mock_setup_entry.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("friendly_name_error", "result_error"),
|
||||
[
|
||||
(ConnectionError, "cannot_connect"),
|
||||
(InvalidPinException, "invalid_auth"),
|
||||
(ValueError, "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_form_nondefault_pin_invalid(
|
||||
hass: HomeAssistant, friendly_name_error: Exception, result_error: str
|
||||
) -> None:
|
||||
"""Test we get the proper errors when trying to validate an user-provided PIN."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"afsapi.AFSAPI.get_friendly_name",
|
||||
side_effect=InvalidPinException,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == FlowResultType.FORM
|
||||
assert result2["step_id"] == "device_config"
|
||||
assert result2["errors"] is None
|
||||
|
||||
with patch(
|
||||
"afsapi.AFSAPI.get_friendly_name",
|
||||
side_effect=friendly_name_error,
|
||||
):
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{CONF_PIN: "4321"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["type"] == FlowResultType.FORM
|
||||
assert result2["step_id"] == "device_config"
|
||||
assert result3["errors"] == {"base": result_error}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("webfsapi_endpoint_error", "result_error"),
|
||||
[
|
||||
(ConnectionError, "cannot_connect"),
|
||||
(ValueError, "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_invalid_device_url(
|
||||
hass: HomeAssistant, webfsapi_endpoint_error: Exception, result_error: str
|
||||
) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"afsapi.AFSAPI.get_webfsapi_endpoint",
|
||||
side_effect=webfsapi_endpoint_error,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == FlowResultType.FORM
|
||||
assert result2["step_id"] == "user"
|
||||
assert result2["errors"] == {"base": result_error}
|
@@ -335,7 +335,7 @@ async def test_ingress_missing_peername(
|
||||
|
||||
|
||||
async def test_forwarding_paths_as_requested(
|
||||
hassio_noauth_client, aioclient_mock
|
||||
hassio_noauth_client, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
"""Test incomnig URLs with double encoding go out as dobule encoded."""
|
||||
# This double encoded string should be forwarded double-encoded too.
|
||||
|
@@ -283,7 +283,7 @@ async def test_alerts(
|
||||
)
|
||||
async def test_alerts_refreshed_on_component_load(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
ha_version,
|
||||
supervisor_info,
|
||||
|
@@ -5,7 +5,7 @@ import json
|
||||
from homeassistant.components.homematicip_cloud.helpers import is_error_response
|
||||
|
||||
|
||||
async def test_is_error_response():
|
||||
async def test_is_error_response() -> None:
|
||||
"""Test, if an response is a normal result or an error."""
|
||||
assert not is_error_response("True")
|
||||
assert not is_error_response(True)
|
||||
|
@@ -12,13 +12,14 @@ from homeassistant.components.lock import (
|
||||
LockEntityFeature,
|
||||
)
|
||||
from homeassistant.const import ATTR_SUPPORTED_FEATURES
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .helper import async_manipulate_test_data, get_and_check_entity_basics
|
||||
|
||||
|
||||
async def test_manually_configured_platform(hass):
|
||||
async def test_manually_configured_platform(hass: HomeAssistant) -> None:
|
||||
"""Test that we do not set up an access point."""
|
||||
assert await async_setup_component(
|
||||
hass, DOMAIN, {DOMAIN: {"platform": HMIPC_DOMAIN}}
|
||||
@@ -26,7 +27,9 @@ async def test_manually_configured_platform(hass):
|
||||
assert not hass.data.get(HMIPC_DOMAIN)
|
||||
|
||||
|
||||
async def test_hmip_doorlockdrive(hass, default_mock_hap_factory):
|
||||
async def test_hmip_doorlockdrive(
|
||||
hass: HomeAssistant, default_mock_hap_factory
|
||||
) -> None:
|
||||
"""Test HomematicipDoorLockDrive."""
|
||||
entity_id = "lock.haustuer"
|
||||
entity_name = "Haustuer"
|
||||
@@ -82,7 +85,9 @@ async def test_hmip_doorlockdrive(hass, default_mock_hap_factory):
|
||||
assert ha_state.state == STATE_UNLOCKING
|
||||
|
||||
|
||||
async def test_hmip_doorlockdrive_handle_errors(hass, default_mock_hap_factory):
|
||||
async def test_hmip_doorlockdrive_handle_errors(
|
||||
hass: HomeAssistant, default_mock_hap_factory
|
||||
) -> None:
|
||||
"""Test HomematicipDoorLockDrive."""
|
||||
entity_id = "lock.haustuer"
|
||||
entity_name = "Haustuer"
|
||||
|
@@ -1,6 +1,8 @@
|
||||
"""Test the jellyfin config flow."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.jellyfin.const import CONF_CLIENT_DEVICE_ID, DOMAIN
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
||||
@@ -11,6 +13,8 @@ from .const import TEST_PASSWORD, TEST_URL, TEST_USERNAME
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
||||
|
||||
async def test_abort_if_existing_entry(hass: HomeAssistant) -> None:
|
||||
"""Check flow abort when an entry already exist."""
|
||||
|
14
tests/components/kmtronic/conftest.py
Normal file
14
tests/components/kmtronic/conftest.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Define fixtures for kmtronic tests."""
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.kmtronic.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
@@ -1,8 +1,9 @@
|
||||
"""Test the kmtronic config flow."""
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from aiohttp import ClientConnectorError, ClientResponseError
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.kmtronic.const import CONF_REVERSE, DOMAIN
|
||||
@@ -12,8 +13,10 @@ from homeassistant.core import HomeAssistant
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
||||
async def test_form(hass: HomeAssistant) -> None:
|
||||
|
||||
async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||
"""Test we get the form."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -25,10 +28,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
with patch(
|
||||
"homeassistant.components.kmtronic.config_flow.KMTronicHubAPI.async_get_status",
|
||||
return_value=[Mock()],
|
||||
), patch(
|
||||
"homeassistant.components.kmtronic.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
@@ -37,6 +37,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
"password": "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == "1.1.1.1"
|
||||
@@ -45,7 +46,6 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
"username": "test-username",
|
||||
"password": "test-password",
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
@@ -47,6 +47,7 @@ async def test_formx(hass: HomeAssistant) -> None:
|
||||
"password": "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_api_class.assert_called_once_with(ANY, "1.1.1.1")
|
||||
mock_api.__aenter__.assert_called_once()
|
||||
@@ -60,7 +61,6 @@ async def test_formx(hass: HomeAssistant) -> None:
|
||||
"host": "1.1.1.1",
|
||||
"password": "test-password",
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
@@ -60,10 +60,10 @@ async def test_flow_no_devices_found(hass: HomeAssistant) -> None:
|
||||
result["flow_id"],
|
||||
{},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "no_devices_found"
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 0
|
||||
|
||||
|
||||
@@ -87,8 +87,8 @@ async def test_flow_exceptions_caught(hass: HomeAssistant) -> None:
|
||||
result["flow_id"],
|
||||
{},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "no_devices_found"
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 0
|
||||
|
14
tests/components/lacrosse_view/conftest.py
Normal file
14
tests/components/lacrosse_view/conftest.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Define fixtures for LaCrosse View tests."""
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.lacrosse_view.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
@@ -1,7 +1,8 @@
|
||||
"""Test the LaCrosse View config flow."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from lacrosse_view import Location, LoginError
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.lacrosse_view.const import DOMAIN
|
||||
@@ -10,8 +11,10 @@ from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
||||
async def test_form(hass: HomeAssistant) -> None:
|
||||
|
||||
async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
@@ -25,8 +28,6 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
), patch(
|
||||
"lacrosse_view.LaCrosse.get_locations",
|
||||
return_value=[Location(id=1, name="Test")],
|
||||
), patch(
|
||||
"homeassistant.components.lacrosse_view.async_setup_entry", return_value=True
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -41,10 +42,6 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
assert result2["step_id"] == "location"
|
||||
assert result2["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lacrosse_view.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{
|
||||
@@ -170,7 +167,9 @@ async def test_form_unexpected_error(hass: HomeAssistant) -> None:
|
||||
assert result2["errors"] == {"base": "unknown"}
|
||||
|
||||
|
||||
async def test_already_configured_device(hass: HomeAssistant) -> None:
|
||||
async def test_already_configured_device(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Test we handle invalid auth."""
|
||||
mock_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
@@ -212,10 +211,6 @@ async def test_already_configured_device(hass: HomeAssistant) -> None:
|
||||
assert result2["step_id"] == "location"
|
||||
assert result2["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lacrosse_view.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{
|
||||
|
15
tests/components/landisgyr_heat_meter/conftest.py
Normal file
15
tests/components/landisgyr_heat_meter/conftest.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""Define fixtures for Landis + Gyr Heat Meter tests."""
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.landisgyr_heat_meter.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
@@ -2,6 +2,7 @@
|
||||
from dataclasses import dataclass
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
|
||||
@@ -14,6 +15,8 @@ from tests.common import MockConfigEntry
|
||||
|
||||
API_HEAT_METER_SERVICE = "homeassistant.components.landisgyr_heat_meter.config_flow.ultraheat_api.HeatMeterService"
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
||||
|
||||
def mock_serial_port():
|
||||
"""Mock of a serial port."""
|
||||
@@ -57,10 +60,6 @@ async def test_manual_entry(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||
assert result["step_id"] == "setup_serial_manual_path"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.landisgyr_heat_meter.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {"device": "/dev/ttyUSB0"}
|
||||
)
|
||||
@@ -122,10 +121,6 @@ async def test_manual_entry_fail(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||
assert result["step_id"] == "setup_serial_manual_path"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.landisgyr_heat_meter.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {"device": "/dev/ttyUSB0"}
|
||||
)
|
||||
|
@@ -3,11 +3,13 @@ from dataclasses import dataclass
|
||||
import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
import serial
|
||||
|
||||
from homeassistant.components.homeassistant import (
|
||||
DOMAIN as HA_DOMAIN,
|
||||
SERVICE_UPDATE_ENTITY,
|
||||
)
|
||||
from homeassistant.components.landisgyr_heat_meter.const import DOMAIN
|
||||
from homeassistant.components.landisgyr_heat_meter.const import DOMAIN, POLLING_INTERVAL
|
||||
from homeassistant.components.sensor import (
|
||||
ATTR_LAST_RESET,
|
||||
ATTR_STATE_CLASS,
|
||||
@@ -19,6 +21,7 @@ from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_ICON,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
STATE_UNAVAILABLE,
|
||||
EntityCategory,
|
||||
UnitOfEnergy,
|
||||
UnitOfVolume,
|
||||
@@ -28,21 +31,29 @@ from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from tests.common import MockConfigEntry, mock_restore_cache_with_extra_data
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
async_fire_time_changed,
|
||||
mock_restore_cache_with_extra_data,
|
||||
)
|
||||
|
||||
API_HEAT_METER_SERVICE = (
|
||||
"homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService"
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MockHeatMeterResponse:
|
||||
"""Mock for HeatMeterResponse."""
|
||||
|
||||
heat_usage_gj: int
|
||||
volume_usage_m3: int
|
||||
heat_previous_year_gj: int
|
||||
heat_usage_gj: float
|
||||
volume_usage_m3: float
|
||||
heat_previous_year_gj: float
|
||||
device_number: str
|
||||
meter_date_time: datetime.datetime
|
||||
|
||||
|
||||
@patch("homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService")
|
||||
@patch(API_HEAT_METER_SERVICE)
|
||||
async def test_create_sensors(
|
||||
mock_heat_meter, hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
) -> None:
|
||||
@@ -57,9 +68,9 @@ async def test_create_sensors(
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
mock_heat_meter_response = MockHeatMeterResponse(
|
||||
heat_usage_gj=123,
|
||||
volume_usage_m3=456,
|
||||
heat_previous_year_gj=111,
|
||||
heat_usage_gj=123.0,
|
||||
volume_usage_m3=456.0,
|
||||
heat_previous_year_gj=111.0,
|
||||
device_number="devicenr_789",
|
||||
meter_date_time=dt_util.as_utc(datetime.datetime(2022, 5, 19, 19, 41, 17)),
|
||||
)
|
||||
@@ -89,7 +100,7 @@ async def test_create_sensors(
|
||||
|
||||
state = hass.states.get("sensor.heat_meter_volume_usage")
|
||||
assert state
|
||||
assert state.state == "456"
|
||||
assert state.state == "456.0"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfVolume.CUBIC_METERS
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.TOTAL
|
||||
|
||||
@@ -110,7 +121,7 @@ async def test_create_sensors(
|
||||
assert entity_registry_entry.entity_category == EntityCategory.DIAGNOSTIC
|
||||
|
||||
|
||||
@patch("homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService")
|
||||
@patch(API_HEAT_METER_SERVICE)
|
||||
async def test_restore_state(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||
"""Test sensor restore state."""
|
||||
# Home assistant is not running yet
|
||||
@@ -199,3 +210,66 @@ async def test_restore_state(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||
assert state
|
||||
assert state.state == "devicenr_789"
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
|
||||
@patch(API_HEAT_METER_SERVICE)
|
||||
async def test_exception_on_polling(mock_heat_meter, hass: HomeAssistant) -> None:
|
||||
"""Test sensor."""
|
||||
entry_data = {
|
||||
"device": "/dev/USB0",
|
||||
"model": "LUGCUH50",
|
||||
"device_number": "123456789",
|
||||
}
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id=DOMAIN, data=entry_data)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
# First setup normally
|
||||
mock_heat_meter_response = MockHeatMeterResponse(
|
||||
heat_usage_gj=123.0,
|
||||
volume_usage_m3=456.0,
|
||||
heat_previous_year_gj=111.0,
|
||||
device_number="devicenr_789",
|
||||
meter_date_time=dt_util.as_utc(datetime.datetime(2022, 5, 19, 19, 41, 17)),
|
||||
)
|
||||
|
||||
mock_heat_meter().read.return_value = mock_heat_meter_response
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await async_setup_component(hass, HA_DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
await hass.services.async_call(
|
||||
HA_DOMAIN,
|
||||
SERVICE_UPDATE_ENTITY,
|
||||
{ATTR_ENTITY_ID: "sensor.heat_meter_heat_usage"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# check if initial setup succeeded
|
||||
state = hass.states.get("sensor.heat_meter_heat_usage")
|
||||
assert state
|
||||
assert state.state == "34.16669"
|
||||
|
||||
# Now 'disable' the connection and wait for polling and see if it fails
|
||||
mock_heat_meter().read.side_effect = serial.serialutil.SerialException
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + POLLING_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.heat_meter_heat_usage")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Now 'enable' and see if next poll succeeds
|
||||
mock_heat_meter_response = MockHeatMeterResponse(
|
||||
heat_usage_gj=124.0,
|
||||
volume_usage_m3=457.0,
|
||||
heat_previous_year_gj=112.0,
|
||||
device_number="devicenr_789",
|
||||
meter_date_time=dt_util.as_utc(datetime.datetime(2022, 5, 19, 20, 41, 17)),
|
||||
)
|
||||
|
||||
mock_heat_meter().read.return_value = mock_heat_meter_response
|
||||
mock_heat_meter().read.side_effect = None
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + POLLING_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.heat_meter_heat_usage")
|
||||
assert state
|
||||
assert state.state == "34.44447"
|
||||
|
@@ -67,13 +67,13 @@ async def test_bridge_import_flow(hass: HomeAssistant) -> None:
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=entry_mock_data,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == CasetaConfigFlow.ENTRY_DEFAULT_TITLE
|
||||
assert result["data"] == entry_mock_data
|
||||
assert result["result"].unique_id == "000004d2"
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
@@ -381,6 +381,8 @@ async def test_import_discovery_integration(
|
||||
type=type_in_discovery,
|
||||
),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == TEST_NAME
|
||||
assert result["data"] == {
|
||||
@@ -395,7 +397,6 @@ async def test_import_discovery_integration(
|
||||
mock_save_json.assert_called_once()
|
||||
mock_remove.assert_not_called()
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@@ -431,6 +432,7 @@ async def test_ssdp_discovery(hass: HomeAssistant) -> None:
|
||||
assert result["step_id"] == "link"
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == TEST_NAME
|
||||
@@ -439,5 +441,4 @@ async def test_ssdp_discovery(hass: HomeAssistant) -> None:
|
||||
CONF_TOKEN: TEST_TOKEN,
|
||||
}
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
@@ -108,7 +108,8 @@ async def test_form_already_configured(
|
||||
result["flow_id"],
|
||||
{"api_key": "test"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "already_configured"
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 0
|
||||
|
@@ -77,6 +77,7 @@ async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> No
|
||||
CONF_PORT: 1234,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@@ -102,8 +103,6 @@ async def test_user_duplicate(
|
||||
)
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("filled_device_registry")
|
||||
|
@@ -1,5 +1,4 @@
|
||||
"""Test the pjlink media player platform."""
|
||||
|
||||
from datetime import timedelta
|
||||
import socket
|
||||
from unittest.mock import create_autospec, patch
|
||||
@@ -11,6 +10,7 @@ import pytest
|
||||
|
||||
import homeassistant.components.media_player as media_player
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt
|
||||
|
||||
@@ -48,7 +48,9 @@ def mocked_projector(projector_from_address):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("side_effect", [socket.timeout, OSError])
|
||||
async def test_offline_initialization(projector_from_address, hass, side_effect):
|
||||
async def test_offline_initialization(
|
||||
projector_from_address, hass: HomeAssistant, side_effect
|
||||
) -> None:
|
||||
"""Test initialization of a device that is offline."""
|
||||
|
||||
with assert_setup_component(1, media_player.DOMAIN):
|
||||
@@ -71,7 +73,7 @@ async def test_offline_initialization(projector_from_address, hass, side_effect)
|
||||
assert state.state == "unavailable"
|
||||
|
||||
|
||||
async def test_initialization(projector_from_address, hass):
|
||||
async def test_initialization(projector_from_address, hass: HomeAssistant) -> None:
|
||||
"""Test a device that is available."""
|
||||
|
||||
with assert_setup_component(1, media_player.DOMAIN):
|
||||
@@ -108,7 +110,9 @@ async def test_initialization(projector_from_address, hass):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("power_state", ["on", "warm-up"])
|
||||
async def test_on_state_init(projector_from_address, hass, power_state):
|
||||
async def test_on_state_init(
|
||||
projector_from_address, hass: HomeAssistant, power_state
|
||||
) -> None:
|
||||
"""Test a device that is available."""
|
||||
|
||||
with assert_setup_component(1, media_player.DOMAIN):
|
||||
@@ -139,7 +143,7 @@ async def test_on_state_init(projector_from_address, hass, power_state):
|
||||
assert state.attributes["source"] == "HDMI 1"
|
||||
|
||||
|
||||
async def test_api_error(projector_from_address, hass):
|
||||
async def test_api_error(projector_from_address, hass: HomeAssistant) -> None:
|
||||
"""Test invalid api responses."""
|
||||
|
||||
with assert_setup_component(1, media_player.DOMAIN):
|
||||
@@ -171,7 +175,7 @@ async def test_api_error(projector_from_address, hass):
|
||||
assert state.state == "off"
|
||||
|
||||
|
||||
async def test_update_unavailable(projector_from_address, hass):
|
||||
async def test_update_unavailable(projector_from_address, hass: HomeAssistant) -> None:
|
||||
"""Test update to a device that is unavailable."""
|
||||
|
||||
with assert_setup_component(1, media_player.DOMAIN):
|
||||
@@ -209,7 +213,7 @@ async def test_update_unavailable(projector_from_address, hass):
|
||||
assert state.state == "unavailable"
|
||||
|
||||
|
||||
async def test_unavailable_time(mocked_projector, hass):
|
||||
async def test_unavailable_time(mocked_projector, hass: HomeAssistant) -> None:
|
||||
"""Test unavailable time projector error."""
|
||||
|
||||
assert await async_setup_component(
|
||||
@@ -240,7 +244,7 @@ async def test_unavailable_time(mocked_projector, hass):
|
||||
assert "is_volume_muted" not in state.attributes
|
||||
|
||||
|
||||
async def test_turn_off(mocked_projector, hass):
|
||||
async def test_turn_off(mocked_projector, hass: HomeAssistant) -> None:
|
||||
"""Test turning off beamer."""
|
||||
|
||||
assert await async_setup_component(
|
||||
@@ -265,7 +269,7 @@ async def test_turn_off(mocked_projector, hass):
|
||||
mocked_projector.set_power.assert_called_with("off")
|
||||
|
||||
|
||||
async def test_turn_on(mocked_projector, hass):
|
||||
async def test_turn_on(mocked_projector, hass: HomeAssistant) -> None:
|
||||
"""Test turning on beamer."""
|
||||
|
||||
assert await async_setup_component(
|
||||
@@ -290,7 +294,7 @@ async def test_turn_on(mocked_projector, hass):
|
||||
mocked_projector.set_power.assert_called_with("on")
|
||||
|
||||
|
||||
async def test_mute(mocked_projector, hass):
|
||||
async def test_mute(mocked_projector, hass: HomeAssistant) -> None:
|
||||
"""Test muting beamer."""
|
||||
|
||||
assert await async_setup_component(
|
||||
@@ -315,7 +319,7 @@ async def test_mute(mocked_projector, hass):
|
||||
mocked_projector.set_mute.assert_called_with(MUTE_AUDIO, True)
|
||||
|
||||
|
||||
async def test_unmute(mocked_projector, hass):
|
||||
async def test_unmute(mocked_projector, hass: HomeAssistant) -> None:
|
||||
"""Test unmuting beamer."""
|
||||
|
||||
assert await async_setup_component(
|
||||
@@ -340,7 +344,7 @@ async def test_unmute(mocked_projector, hass):
|
||||
mocked_projector.set_mute.assert_called_with(MUTE_AUDIO, False)
|
||||
|
||||
|
||||
async def test_select_source(mocked_projector, hass):
|
||||
async def test_select_source(mocked_projector, hass: HomeAssistant) -> None:
|
||||
"""Test selecting source."""
|
||||
|
||||
assert await async_setup_component(
|
||||
|
@@ -9,10 +9,11 @@ from homeassistant.components import camera
|
||||
from homeassistant.components.camera import Image
|
||||
from homeassistant.components.prosegur.const import DOMAIN
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
|
||||
async def test_camera(hass, init_integration):
|
||||
async def test_camera(hass: HomeAssistant, init_integration) -> None:
|
||||
"""Test prosegur get_image."""
|
||||
|
||||
image = await camera.async_get_image(hass, "camera.test_cam")
|
||||
@@ -20,7 +21,12 @@ async def test_camera(hass, init_integration):
|
||||
assert image == Image(content_type="image/jpeg", content=b"ABC")
|
||||
|
||||
|
||||
async def test_camera_fail(hass, init_integration, mock_install, caplog):
|
||||
async def test_camera_fail(
|
||||
hass: HomeAssistant,
|
||||
init_integration,
|
||||
mock_install,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test prosegur get_image fails."""
|
||||
|
||||
mock_install.get_image = AsyncMock(
|
||||
@@ -37,7 +43,9 @@ async def test_camera_fail(hass, init_integration, mock_install, caplog):
|
||||
assert "Image test_cam doesn't exist" in caplog.text
|
||||
|
||||
|
||||
async def test_request_image(hass, init_integration, mock_install):
|
||||
async def test_request_image(
|
||||
hass: HomeAssistant, init_integration, mock_install
|
||||
) -> None:
|
||||
"""Test the camera request image service."""
|
||||
|
||||
await hass.services.async_call(
|
||||
@@ -50,7 +58,12 @@ async def test_request_image(hass, init_integration, mock_install):
|
||||
assert mock_install.request_image.called
|
||||
|
||||
|
||||
async def test_request_image_fail(hass, init_integration, mock_install, caplog):
|
||||
async def test_request_image_fail(
|
||||
hass: HomeAssistant,
|
||||
init_integration,
|
||||
mock_install,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test the camera request image service fails."""
|
||||
|
||||
mock_install.request_image = AsyncMock(side_effect=ProsegurException())
|
||||
|
@@ -1,11 +1,18 @@
|
||||
"""Test Prosegur diagnostics."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
async def test_diagnostics(hass, hass_client, init_integration, mock_install):
|
||||
async def test_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
init_integration,
|
||||
mock_install,
|
||||
) -> None:
|
||||
"""Test generating diagnostics for a config entry."""
|
||||
|
||||
with patch(
|
||||
|
@@ -1805,7 +1805,7 @@ def record_states(hass):
|
||||
return zero, four, states
|
||||
|
||||
|
||||
def test_cache_key_for_generate_statistics_during_period_stmt():
|
||||
def test_cache_key_for_generate_statistics_during_period_stmt() -> None:
|
||||
"""Test cache key for _generate_statistics_during_period_stmt."""
|
||||
columns = select(StatisticsShortTerm.metadata_id, StatisticsShortTerm.start_ts)
|
||||
stmt = _generate_statistics_during_period_stmt(
|
||||
@@ -1835,7 +1835,7 @@ def test_cache_key_for_generate_statistics_during_period_stmt():
|
||||
assert cache_key_1 != cache_key_3
|
||||
|
||||
|
||||
def test_cache_key_for_generate_get_metadata_stmt():
|
||||
def test_cache_key_for_generate_get_metadata_stmt() -> None:
|
||||
"""Test cache key for _generate_get_metadata_stmt."""
|
||||
stmt_mean = _generate_get_metadata_stmt([0], "mean")
|
||||
stmt_mean2 = _generate_get_metadata_stmt([1], "mean")
|
||||
@@ -1846,7 +1846,7 @@ def test_cache_key_for_generate_get_metadata_stmt():
|
||||
assert stmt_mean._generate_cache_key() != stmt_none._generate_cache_key()
|
||||
|
||||
|
||||
def test_cache_key_for_generate_max_mean_min_statistic_in_sub_period_stmt():
|
||||
def test_cache_key_for_generate_max_mean_min_statistic_in_sub_period_stmt() -> None:
|
||||
"""Test cache key for _generate_max_mean_min_statistic_in_sub_period_stmt."""
|
||||
columns = select(StatisticsShortTerm.metadata_id, StatisticsShortTerm.start_ts)
|
||||
stmt = _generate_max_mean_min_statistic_in_sub_period_stmt(
|
||||
@@ -1883,7 +1883,7 @@ def test_cache_key_for_generate_max_mean_min_statistic_in_sub_period_stmt():
|
||||
assert cache_key_1 != cache_key_3
|
||||
|
||||
|
||||
def test_cache_key_for_generate_statistics_at_time_stmt():
|
||||
def test_cache_key_for_generate_statistics_at_time_stmt() -> None:
|
||||
"""Test cache key for _generate_statistics_at_time_stmt."""
|
||||
columns = select(StatisticsShortTerm.metadata_id, StatisticsShortTerm.start_ts)
|
||||
stmt = _generate_statistics_at_time_stmt(columns, StatisticsShortTerm, {0}, 0.0)
|
||||
|
@@ -4,7 +4,7 @@ import json
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from sfrbox_api.models import DslInfo, SystemInfo
|
||||
from sfrbox_api.models import DslInfo, FtthInfo, SystemInfo, WanInfo
|
||||
|
||||
from homeassistant.components.sfr_box.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER, ConfigEntry
|
||||
@@ -57,17 +57,6 @@ def get_config_entry_with_auth(hass: HomeAssistant) -> ConfigEntry:
|
||||
return config_entry_with_auth
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def system_get_info() -> Generator[SystemInfo, None, None]:
|
||||
"""Fixture for SFRBox.system_get_info."""
|
||||
system_info = SystemInfo(**json.loads(load_fixture("system_getInfo.json", DOMAIN)))
|
||||
with patch(
|
||||
"homeassistant.components.sfr_box.coordinator.SFRBox.system_get_info",
|
||||
return_value=system_info,
|
||||
):
|
||||
yield system_info
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dsl_get_info() -> Generator[DslInfo, None, None]:
|
||||
"""Fixture for SFRBox.dsl_get_info."""
|
||||
@@ -77,3 +66,36 @@ def dsl_get_info() -> Generator[DslInfo, None, None]:
|
||||
return_value=dsl_info,
|
||||
):
|
||||
yield dsl_info
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ftth_get_info() -> Generator[FtthInfo, None, None]:
|
||||
"""Fixture for SFRBox.ftth_get_info."""
|
||||
info = FtthInfo(**json.loads(load_fixture("ftth_getInfo.json", DOMAIN)))
|
||||
with patch(
|
||||
"homeassistant.components.sfr_box.coordinator.SFRBox.ftth_get_info",
|
||||
return_value=info,
|
||||
):
|
||||
yield info
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def system_get_info() -> Generator[SystemInfo, None, None]:
|
||||
"""Fixture for SFRBox.system_get_info."""
|
||||
info = SystemInfo(**json.loads(load_fixture("system_getInfo.json", DOMAIN)))
|
||||
with patch(
|
||||
"homeassistant.components.sfr_box.coordinator.SFRBox.system_get_info",
|
||||
return_value=info,
|
||||
):
|
||||
yield info
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wan_get_info() -> Generator[WanInfo, None, None]:
|
||||
"""Fixture for SFRBox.wan_get_info."""
|
||||
info = WanInfo(**json.loads(load_fixture("wan_getInfo.json", DOMAIN)))
|
||||
with patch(
|
||||
"homeassistant.components.sfr_box.coordinator.SFRBox.wan_get_info",
|
||||
return_value=info,
|
||||
):
|
||||
yield info
|
||||
|
4
tests/components/sfr_box/fixtures/ftth_getInfo.json
Normal file
4
tests/components/sfr_box/fixtures/ftth_getInfo.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"status": "down",
|
||||
"wanfibre": "out"
|
||||
}
|
11
tests/components/sfr_box/fixtures/wan_getInfo.json
Normal file
11
tests/components/sfr_box/fixtures/wan_getInfo.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"status": "up",
|
||||
"uptime": 297464,
|
||||
"ip_addr": "1.2.3.4",
|
||||
"infra": "adsl",
|
||||
"mode": "adsl/routed",
|
||||
"infra6": "",
|
||||
"status6": "down",
|
||||
"uptime6": null,
|
||||
"ipv6_addr": ""
|
||||
}
|
60
tests/components/sfr_box/snapshots/test_diagnostics.ambr
Normal file
60
tests/components/sfr_box/snapshots/test_diagnostics.ambr
Normal file
@@ -0,0 +1,60 @@
|
||||
# serializer version: 1
|
||||
# name: test_entry_diagnostics
|
||||
dict({
|
||||
'data': dict({
|
||||
'dsl': dict({
|
||||
'attenuation_down': 28.5,
|
||||
'attenuation_up': 20.8,
|
||||
'counter': 16,
|
||||
'crc': 0,
|
||||
'line_status': 'No Defect',
|
||||
'linemode': 'ADSL2+',
|
||||
'noise_down': 5.8,
|
||||
'noise_up': 6.0,
|
||||
'rate_down': 5549,
|
||||
'rate_up': 187,
|
||||
'status': 'up',
|
||||
'training': 'Showtime',
|
||||
'uptime': 450796,
|
||||
}),
|
||||
'ftth': dict({
|
||||
'status': 'down',
|
||||
'wanfibre': 'out',
|
||||
}),
|
||||
'system': dict({
|
||||
'alimvoltage': 12251,
|
||||
'current_datetime': '202212282233',
|
||||
'idur': 'RP3P85K',
|
||||
'mac_addr': '**REDACTED**',
|
||||
'net_infra': 'adsl',
|
||||
'net_mode': 'router',
|
||||
'product_id': 'NB6VAC-FXC-r0',
|
||||
'refclient': '',
|
||||
'serial_number': '**REDACTED**',
|
||||
'temperature': 27560,
|
||||
'uptime': 2353575,
|
||||
'version_bootloader': 'NB6VAC-BOOTLOADER-R4.0.8',
|
||||
'version_dsldriver': 'NB6VAC-XDSL-A2pv6F039p',
|
||||
'version_mainfirmware': 'NB6VAC-MAIN-R4.0.44k',
|
||||
'version_rescuefirmware': 'NB6VAC-MAIN-R4.0.44k',
|
||||
}),
|
||||
'wan': dict({
|
||||
'infra': 'adsl',
|
||||
'infra6': '',
|
||||
'ip_addr': '1.2.3.4',
|
||||
'ipv6_addr': '',
|
||||
'mode': 'adsl/routed',
|
||||
'status': 'up',
|
||||
'status6': 'down',
|
||||
'uptime': 297464,
|
||||
'uptime6': None,
|
||||
}),
|
||||
}),
|
||||
'entry': dict({
|
||||
'data': dict({
|
||||
'host': '192.168.0.1',
|
||||
}),
|
||||
'title': 'Mock Title',
|
||||
}),
|
||||
})
|
||||
# ---
|
@@ -3,15 +3,17 @@ from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.diagnostics import REDACTED
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("system_get_info", "dsl_get_info")
|
||||
pytestmark = pytest.mark.usefixtures(
|
||||
"dsl_get_info", "ftth_get_info", "system_get_info", "wan_get_info"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -22,49 +24,16 @@ def override_platforms() -> Generator[None, None, None]:
|
||||
|
||||
|
||||
async def test_entry_diagnostics(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, hass_client: ClientSessionGenerator
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
hass_client: ClientSessionGenerator,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test config entry diagnostics."""
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
|
||||
"entry": {
|
||||
"data": {"host": "192.168.0.1"},
|
||||
"title": "Mock Title",
|
||||
},
|
||||
"data": {
|
||||
"dsl": {
|
||||
"attenuation_down": 28.5,
|
||||
"attenuation_up": 20.8,
|
||||
"counter": 16,
|
||||
"crc": 0,
|
||||
"line_status": "No Defect",
|
||||
"linemode": "ADSL2+",
|
||||
"noise_down": 5.8,
|
||||
"noise_up": 6.0,
|
||||
"rate_down": 5549,
|
||||
"rate_up": 187,
|
||||
"status": "up",
|
||||
"training": "Showtime",
|
||||
"uptime": 450796,
|
||||
},
|
||||
"system": {
|
||||
"alimvoltage": 12251,
|
||||
"current_datetime": "202212282233",
|
||||
"idur": "RP3P85K",
|
||||
"mac_addr": REDACTED,
|
||||
"net_infra": "adsl",
|
||||
"net_mode": "router",
|
||||
"product_id": "NB6VAC-FXC-r0",
|
||||
"refclient": "",
|
||||
"serial_number": REDACTED,
|
||||
"temperature": 27560,
|
||||
"uptime": 2353575,
|
||||
"version_bootloader": "NB6VAC-BOOTLOADER-R4.0.8",
|
||||
"version_dsldriver": "NB6VAC-XDSL-A2pv6F039p",
|
||||
"version_mainfirmware": "NB6VAC-MAIN-R4.0.44k",
|
||||
"version_rescuefirmware": "NB6VAC-MAIN-R4.0.44k",
|
||||
},
|
||||
},
|
||||
}
|
||||
assert (
|
||||
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
|
||||
== snapshot
|
||||
)
|
||||
|
@@ -119,10 +119,10 @@ async def test_success(
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
data=FIXTURE_USER_INPUT,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["data"][CONF_URL] == FIXTURE_USER_INPUT[CONF_URL]
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
@@ -225,7 +225,7 @@ async def test_template_position(hass: HomeAssistant, start_ha) -> None:
|
||||
},
|
||||
],
|
||||
)
|
||||
async def test_template_not_optimistic(hass, start_ha):
|
||||
async def test_template_not_optimistic(hass: HomeAssistant, start_ha) -> None:
|
||||
"""Test the is_closed attribute."""
|
||||
state = hass.states.get("cover.test_template_cover")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
@@ -185,3 +185,109 @@ ROUTER_DISCOVERY_HASS_MISSING_MANDATORY_DATA = {
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
||||
|
||||
ROUTER_DISCOVERY_HASS_NO_ACTIVE_TIMESTAMP = {
|
||||
"type_": "_meshcop._udp.local.",
|
||||
"name": "HomeAssistant OpenThreadBorderRouter #0BBF._meshcop._udp.local.",
|
||||
"addresses": [b"\xc0\xa8\x00s"],
|
||||
"port": 49153,
|
||||
"weight": 0,
|
||||
"priority": 0,
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"properties": {
|
||||
b"rv": b"1",
|
||||
b"vn": b"HomeAssistant",
|
||||
b"mn": b"OpenThreadBorderRouter",
|
||||
b"nn": b"OpenThread HC",
|
||||
b"xp": b"\xe6\x0f\xc7\xc1\x86!,\xe5",
|
||||
b"tv": b"1.3.0",
|
||||
b"xa": b"\xae\xeb/YKW\x0b\xbf",
|
||||
b"sb": b"\x00\x00\x01\xb1",
|
||||
b"pt": b"\x8f\x06Q~",
|
||||
b"sq": b"3",
|
||||
b"bb": b"\xf0\xbf",
|
||||
b"dn": b"DefaultDomain",
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
||||
|
||||
ROUTER_DISCOVERY_HASS_NO_STATE_BITMAP = {
|
||||
"type_": "_meshcop._udp.local.",
|
||||
"name": "HomeAssistant OpenThreadBorderRouter #0BBF._meshcop._udp.local.",
|
||||
"addresses": [b"\xc0\xa8\x00s"],
|
||||
"port": 49153,
|
||||
"weight": 0,
|
||||
"priority": 0,
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"properties": {
|
||||
b"rv": b"1",
|
||||
b"vn": b"HomeAssistant",
|
||||
b"mn": b"OpenThreadBorderRouter",
|
||||
b"nn": b"OpenThread HC",
|
||||
b"xp": b"\xe6\x0f\xc7\xc1\x86!,\xe5",
|
||||
b"tv": b"1.3.0",
|
||||
b"xa": b"\xae\xeb/YKW\x0b\xbf",
|
||||
b"at": b"\x00\x00\x00\x00\x00\x01\x00\x00",
|
||||
b"pt": b"\x8f\x06Q~",
|
||||
b"sq": b"3",
|
||||
b"bb": b"\xf0\xbf",
|
||||
b"dn": b"DefaultDomain",
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
||||
|
||||
ROUTER_DISCOVERY_HASS_BAD_STATE_BITMAP = {
|
||||
"type_": "_meshcop._udp.local.",
|
||||
"name": "HomeAssistant OpenThreadBorderRouter #0BBF._meshcop._udp.local.",
|
||||
"addresses": [b"\xc0\xa8\x00s"],
|
||||
"port": 49153,
|
||||
"weight": 0,
|
||||
"priority": 0,
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"properties": {
|
||||
b"rv": b"1",
|
||||
b"vn": b"HomeAssistant",
|
||||
b"mn": b"OpenThreadBorderRouter",
|
||||
b"nn": b"OpenThread HC",
|
||||
b"xp": b"\xe6\x0f\xc7\xc1\x86!,\xe5",
|
||||
b"tv": b"1.3.0",
|
||||
b"xa": b"\xae\xeb/YKW\x0b\xbf",
|
||||
b"sb": b"\xff\x00\x01\xb1",
|
||||
b"at": b"\x00\x00\x00\x00\x00\x01\x00\x00",
|
||||
b"pt": b"\x8f\x06Q~",
|
||||
b"sq": b"3",
|
||||
b"bb": b"\xf0\xbf",
|
||||
b"dn": b"DefaultDomain",
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
||||
|
||||
ROUTER_DISCOVERY_HASS_STATE_BITMAP_NOT_ACTIVE = {
|
||||
"type_": "_meshcop._udp.local.",
|
||||
"name": "HomeAssistant OpenThreadBorderRouter #0BBF._meshcop._udp.local.",
|
||||
"addresses": [b"\xc0\xa8\x00s"],
|
||||
"port": 49153,
|
||||
"weight": 0,
|
||||
"priority": 0,
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"properties": {
|
||||
b"rv": b"1",
|
||||
b"vn": b"HomeAssistant",
|
||||
b"mn": b"OpenThreadBorderRouter",
|
||||
b"nn": b"OpenThread HC",
|
||||
b"xp": b"\xe6\x0f\xc7\xc1\x86!,\xe5",
|
||||
b"tv": b"1.3.0",
|
||||
b"xa": b"\xae\xeb/YKW\x0b\xbf",
|
||||
b"sb": b"\x00\x00\x01\x31",
|
||||
b"at": b"\x00\x00\x00\x00\x00\x01\x00\x00",
|
||||
b"pt": b"\x8f\x06Q~",
|
||||
b"sq": b"3",
|
||||
b"bb": b"\xf0\xbf",
|
||||
b"dn": b"DefaultDomain",
|
||||
},
|
||||
"interface_index": None,
|
||||
}
|
||||
|
@@ -14,8 +14,12 @@ from . import (
|
||||
ROUTER_DISCOVERY_GOOGLE_1,
|
||||
ROUTER_DISCOVERY_HASS,
|
||||
ROUTER_DISCOVERY_HASS_BAD_DATA,
|
||||
ROUTER_DISCOVERY_HASS_BAD_STATE_BITMAP,
|
||||
ROUTER_DISCOVERY_HASS_MISSING_DATA,
|
||||
ROUTER_DISCOVERY_HASS_MISSING_MANDATORY_DATA,
|
||||
ROUTER_DISCOVERY_HASS_NO_ACTIVE_TIMESTAMP,
|
||||
ROUTER_DISCOVERY_HASS_NO_STATE_BITMAP,
|
||||
ROUTER_DISCOVERY_HASS_STATE_BITMAP_NOT_ACTIVE,
|
||||
)
|
||||
|
||||
|
||||
@@ -67,14 +71,15 @@ async def test_discover_routers(hass: HomeAssistant, mock_async_zeroconf: None)
|
||||
assert discovered[-1] == (
|
||||
"aeeb2f594b570bbf",
|
||||
discovery.ThreadRouterDiscoveryData(
|
||||
addresses=["192.168.0.115"],
|
||||
brand="homeassistant",
|
||||
extended_pan_id="e60fc7c186212ce5",
|
||||
model_name="OpenThreadBorderRouter",
|
||||
network_name="OpenThread HC",
|
||||
server="core-silabs-multiprotocol.local.",
|
||||
vendor_name="HomeAssistant",
|
||||
thread_version="1.3.0",
|
||||
addresses=["192.168.0.115"],
|
||||
unconfigured=None,
|
||||
vendor_name="HomeAssistant",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -91,14 +96,15 @@ async def test_discover_routers(hass: HomeAssistant, mock_async_zeroconf: None)
|
||||
assert discovered[-1] == (
|
||||
"f6a99b425a67abed",
|
||||
discovery.ThreadRouterDiscoveryData(
|
||||
addresses=["192.168.0.124"],
|
||||
brand="google",
|
||||
extended_pan_id="9e75e256f61409a3",
|
||||
model_name="Google Nest Hub",
|
||||
network_name="NEST-PAN-E1AF",
|
||||
server="2d99f293-cd8e-2770-8dd2-6675de9fa000.local.",
|
||||
vendor_name="Google Inc.",
|
||||
thread_version="1.3.0",
|
||||
addresses=["192.168.0.124"],
|
||||
unconfigured=None,
|
||||
vendor_name="Google Inc.",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -130,6 +136,56 @@ async def test_discover_routers(hass: HomeAssistant, mock_async_zeroconf: None)
|
||||
mock_async_zeroconf.async_remove_service_listener.assert_called_once_with(listener)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("data", "unconfigured"),
|
||||
[
|
||||
(ROUTER_DISCOVERY_HASS_NO_ACTIVE_TIMESTAMP, True),
|
||||
(ROUTER_DISCOVERY_HASS_BAD_STATE_BITMAP, None),
|
||||
(ROUTER_DISCOVERY_HASS_NO_STATE_BITMAP, None),
|
||||
(ROUTER_DISCOVERY_HASS_STATE_BITMAP_NOT_ACTIVE, True),
|
||||
],
|
||||
)
|
||||
async def test_discover_routers_unconfigured(
|
||||
hass: HomeAssistant, mock_async_zeroconf: None, data, unconfigured
|
||||
) -> None:
|
||||
"""Test discovering thread routers with bad or missing vendor mDNS data."""
|
||||
mock_async_zeroconf.async_add_service_listener = AsyncMock()
|
||||
mock_async_zeroconf.async_remove_service_listener = AsyncMock()
|
||||
mock_async_zeroconf.async_get_service_info = AsyncMock()
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Start Thread router discovery
|
||||
router_discovered_removed = Mock()
|
||||
thread_disovery = discovery.ThreadRouterDiscovery(
|
||||
hass, router_discovered_removed, router_discovered_removed
|
||||
)
|
||||
await thread_disovery.async_start()
|
||||
listener: discovery.ThreadRouterDiscovery.ThreadServiceListener = (
|
||||
mock_async_zeroconf.async_add_service_listener.mock_calls[0][1][1]
|
||||
)
|
||||
|
||||
# Discover a service with bad or missing data
|
||||
mock_async_zeroconf.async_get_service_info.return_value = AsyncServiceInfo(**data)
|
||||
listener.add_service(None, data["type_"], data["name"])
|
||||
await hass.async_block_till_done()
|
||||
router_discovered_removed.assert_called_once_with(
|
||||
"aeeb2f594b570bbf",
|
||||
discovery.ThreadRouterDiscoveryData(
|
||||
addresses=["192.168.0.115"],
|
||||
brand="homeassistant",
|
||||
extended_pan_id="e60fc7c186212ce5",
|
||||
model_name="OpenThreadBorderRouter",
|
||||
network_name="OpenThread HC",
|
||||
server="core-silabs-multiprotocol.local.",
|
||||
thread_version="1.3.0",
|
||||
unconfigured=unconfigured,
|
||||
vendor_name="HomeAssistant",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data", (ROUTER_DISCOVERY_HASS_BAD_DATA, ROUTER_DISCOVERY_HASS_MISSING_DATA)
|
||||
)
|
||||
@@ -161,14 +217,15 @@ async def test_discover_routers_bad_data(
|
||||
router_discovered_removed.assert_called_once_with(
|
||||
"aeeb2f594b570bbf",
|
||||
discovery.ThreadRouterDiscoveryData(
|
||||
addresses=["192.168.0.115"],
|
||||
brand=None,
|
||||
extended_pan_id="e60fc7c186212ce5",
|
||||
model_name="OpenThreadBorderRouter",
|
||||
network_name="OpenThread HC",
|
||||
server="core-silabs-multiprotocol.local.",
|
||||
vendor_name=None,
|
||||
thread_version="1.3.0",
|
||||
addresses=["192.168.0.115"],
|
||||
unconfigured=None,
|
||||
vendor_name=None,
|
||||
),
|
||||
)
|
||||
|
||||
|
@@ -234,14 +234,15 @@ async def test_discover_routers(
|
||||
assert msg == {
|
||||
"event": {
|
||||
"data": {
|
||||
"addresses": ["192.168.0.115"],
|
||||
"brand": "homeassistant",
|
||||
"extended_pan_id": "e60fc7c186212ce5",
|
||||
"model_name": "OpenThreadBorderRouter",
|
||||
"network_name": "OpenThread HC",
|
||||
"server": "core-silabs-multiprotocol.local.",
|
||||
"vendor_name": "HomeAssistant",
|
||||
"addresses": ["192.168.0.115"],
|
||||
"thread_version": "1.3.0",
|
||||
"unconfigured": None,
|
||||
"vendor_name": "HomeAssistant",
|
||||
},
|
||||
"key": "aeeb2f594b570bbf",
|
||||
"type": "router_discovered",
|
||||
@@ -261,14 +262,15 @@ async def test_discover_routers(
|
||||
assert msg == {
|
||||
"event": {
|
||||
"data": {
|
||||
"addresses": ["192.168.0.124"],
|
||||
"brand": "google",
|
||||
"extended_pan_id": "9e75e256f61409a3",
|
||||
"model_name": "Google Nest Hub",
|
||||
"network_name": "NEST-PAN-E1AF",
|
||||
"server": "2d99f293-cd8e-2770-8dd2-6675de9fa000.local.",
|
||||
"vendor_name": "Google Inc.",
|
||||
"thread_version": "1.3.0",
|
||||
"addresses": ["192.168.0.124"],
|
||||
"unconfigured": None,
|
||||
"vendor_name": "Google Inc.",
|
||||
},
|
||||
"key": "f6a99b425a67abed",
|
||||
"type": "router_discovered",
|
||||
|
@@ -70,7 +70,9 @@ async def test_create_entry(recorder_mock: Recorder, hass: HomeAssistant) -> Non
|
||||
(FatalHttpException(404), ERR_CLIENT),
|
||||
],
|
||||
)
|
||||
async def test_create_entry_exceptions(recorder_mock, hass, exception, expected_error):
|
||||
async def test_create_entry_exceptions(
|
||||
recorder_mock: Recorder, hass: HomeAssistant, exception, expected_error
|
||||
) -> None:
|
||||
"""Test create entry from user input."""
|
||||
test_data = {
|
||||
CONF_ACCESS_TOKEN: "valid",
|
||||
@@ -93,7 +95,9 @@ async def test_create_entry_exceptions(recorder_mock, hass, exception, expected_
|
||||
assert result["errors"][CONF_ACCESS_TOKEN] == expected_error
|
||||
|
||||
|
||||
async def test_flow_entry_already_exists(recorder_mock, hass, config_entry):
|
||||
async def test_flow_entry_already_exists(
|
||||
recorder_mock: Recorder, hass: HomeAssistant, config_entry
|
||||
) -> None:
|
||||
"""Test user input for config_entry that already exists."""
|
||||
test_data = {
|
||||
CONF_ACCESS_TOKEN: "valid",
|
||||
|
@@ -91,7 +91,9 @@ async def test_calendar_entity_unique_id(
|
||||
|
||||
|
||||
@patch("homeassistant.components.todoist.calendar.TodoistAPIAsync")
|
||||
async def test_update_entity_for_custom_project_with_labels_on(todoist_api, hass, api):
|
||||
async def test_update_entity_for_custom_project_with_labels_on(
|
||||
todoist_api, hass: HomeAssistant, api
|
||||
) -> None:
|
||||
"""Test that the calendar's state is on for a custom project using labels."""
|
||||
todoist_api.return_value = api
|
||||
assert await setup.async_setup_component(
|
||||
|
@@ -1,5 +1,4 @@
|
||||
"""Tests for the Twente Milieu sensors."""
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
|
@@ -1103,7 +1103,7 @@ async def test_state_template(hass: HomeAssistant) -> None:
|
||||
assert hass.states.get("media_player.tv").state == STATE_OFF
|
||||
|
||||
|
||||
async def test_browse_media(hass: HomeAssistant):
|
||||
async def test_browse_media(hass: HomeAssistant) -> None:
|
||||
"""Test browse media."""
|
||||
await async_setup_component(
|
||||
hass, "media_player", {"media_player": {"platform": "demo"}}
|
||||
@@ -1133,7 +1133,7 @@ async def test_browse_media(hass: HomeAssistant):
|
||||
assert result == MOCK_BROWSE_MEDIA
|
||||
|
||||
|
||||
async def test_browse_media_override(hass: HomeAssistant):
|
||||
async def test_browse_media_override(hass: HomeAssistant) -> None:
|
||||
"""Test browse media override."""
|
||||
await async_setup_component(
|
||||
hass, "media_player", {"media_player": {"platform": "demo"}}
|
||||
|
@@ -3,8 +3,12 @@ from homeassistant.components.weather.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
async def test_device_class_units(hass: HomeAssistant, hass_ws_client) -> None:
|
||||
|
||||
async def test_device_class_units(
|
||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
|
||||
) -> None:
|
||||
"""Test we can get supported units."""
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
|
@@ -306,8 +306,8 @@ async def test_gateway_initialize_failure_transient(
|
||||
],
|
||||
)
|
||||
async def test_gateway_initialize_bellows_thread(
|
||||
device_path, thread_state, config_override, hass, coordinator
|
||||
):
|
||||
device_path, thread_state, config_override, hass: HomeAssistant, coordinator
|
||||
) -> None:
|
||||
"""Test ZHA disabling the UART thread when connecting to a TCP coordinator."""
|
||||
zha_gateway = get_zha_gateway(hass)
|
||||
assert zha_gateway is not None
|
||||
|
@@ -323,7 +323,7 @@ def test_weighted_match(
|
||||
model,
|
||||
quirk_class,
|
||||
match_name,
|
||||
):
|
||||
) -> None:
|
||||
"""Test weightedd match."""
|
||||
|
||||
s = mock.sentinel
|
||||
@@ -435,7 +435,7 @@ def test_multi_sensor_match(channel, entity_registry: er.EntityRegistry) -> None
|
||||
}
|
||||
|
||||
|
||||
def test_quirk_classes():
|
||||
def test_quirk_classes() -> None:
|
||||
"""Make sure that quirk_classes in components matches are valid."""
|
||||
|
||||
def find_quirk_class(base_obj, quirk_mod, quirk_cls):
|
||||
|
@@ -1460,8 +1460,8 @@ async def test_parse_qr_code_string(
|
||||
|
||||
|
||||
async def test_try_parse_dsk_from_qr_code_string(
|
||||
hass, integration, client, hass_ws_client
|
||||
):
|
||||
hass: HomeAssistant, integration, client, hass_ws_client: WebSocketGenerator
|
||||
) -> None:
|
||||
"""Test try_parse_dsk_from_qr_code_string websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
@@ -1524,7 +1524,9 @@ async def test_try_parse_dsk_from_qr_code_string(
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_supports_feature(hass, integration, client, hass_ws_client):
|
||||
async def test_supports_feature(
|
||||
hass: HomeAssistant, integration, client, hass_ws_client: WebSocketGenerator
|
||||
) -> None:
|
||||
"""Test supports_feature websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
@@ -3888,8 +3890,8 @@ async def test_subscribe_firmware_update_status_initial_value(
|
||||
|
||||
|
||||
async def test_subscribe_controller_firmware_update_status(
|
||||
hass, integration, client, hass_ws_client
|
||||
):
|
||||
hass: HomeAssistant, integration, client, hass_ws_client: WebSocketGenerator
|
||||
) -> None:
|
||||
"""Test the subscribe_firmware_update_status websocket command for a node."""
|
||||
ws_client = await hass_ws_client(hass)
|
||||
device = get_device(hass, client.driver.controller.nodes[1])
|
||||
@@ -3954,8 +3956,8 @@ async def test_subscribe_controller_firmware_update_status(
|
||||
|
||||
|
||||
async def test_subscribe_controller_firmware_update_status_initial_value(
|
||||
hass, client, integration, hass_ws_client
|
||||
):
|
||||
hass: HomeAssistant, client, integration, hass_ws_client: WebSocketGenerator
|
||||
) -> None:
|
||||
"""Test subscribe_firmware_update_status cmd with in progress update for node."""
|
||||
ws_client = await hass_ws_client(hass)
|
||||
device = get_device(hass, client.driver.controller.nodes[1])
|
||||
|
@@ -103,7 +103,9 @@ async def test_dynamic_climate_data_discovery_template_failure(
|
||||
)
|
||||
|
||||
|
||||
async def test_merten_507801(hass, client, merten_507801, integration):
|
||||
async def test_merten_507801(
|
||||
hass: HomeAssistant, client, merten_507801, integration
|
||||
) -> None:
|
||||
"""Test that Merten 507801 multilevel switch value is discovered as a cover."""
|
||||
node = merten_507801
|
||||
assert node.device_class.specific.label == "Unused"
|
||||
@@ -116,8 +118,8 @@ async def test_merten_507801(hass, client, merten_507801, integration):
|
||||
|
||||
|
||||
async def test_merten_507801_disabled_enitites(
|
||||
hass, client, merten_507801, integration
|
||||
):
|
||||
hass: HomeAssistant, client, merten_507801, integration
|
||||
) -> None:
|
||||
"""Test that Merten 507801 entities created by endpoint 2 are disabled."""
|
||||
registry = er.async_get(hass)
|
||||
entity_ids = [
|
||||
|
@@ -223,7 +223,7 @@ def area_mock(hass):
|
||||
)
|
||||
|
||||
|
||||
async def test_call_from_config(hass: HomeAssistant):
|
||||
async def test_call_from_config(hass: HomeAssistant) -> None:
|
||||
"""Test the sync wrapper of service.async_call_from_config."""
|
||||
calls = async_mock_service(hass, "test_domain", "test_service")
|
||||
config = {
|
||||
@@ -238,7 +238,7 @@ async def test_call_from_config(hass: HomeAssistant):
|
||||
assert calls[0].data == {"hello": "goodbye", "entity_id": ["hello.world"]}
|
||||
|
||||
|
||||
async def test_service_call(hass: HomeAssistant):
|
||||
async def test_service_call(hass: HomeAssistant) -> None:
|
||||
"""Test service call with templating."""
|
||||
calls = async_mock_service(hass, "test_domain", "test_service")
|
||||
config = {
|
||||
@@ -307,7 +307,7 @@ async def test_service_call(hass: HomeAssistant):
|
||||
}
|
||||
|
||||
|
||||
async def test_service_template_service_call(hass: HomeAssistant):
|
||||
async def test_service_template_service_call(hass: HomeAssistant) -> None:
|
||||
"""Test legacy service_template call with templating."""
|
||||
calls = async_mock_service(hass, "test_domain", "test_service")
|
||||
config = {
|
||||
@@ -322,7 +322,7 @@ async def test_service_template_service_call(hass: HomeAssistant):
|
||||
assert calls[0].data == {"hello": "goodbye", "entity_id": ["hello.world"]}
|
||||
|
||||
|
||||
async def test_passing_variables_to_templates(hass: HomeAssistant):
|
||||
async def test_passing_variables_to_templates(hass: HomeAssistant) -> None:
|
||||
"""Test passing variables to templates."""
|
||||
calls = async_mock_service(hass, "test_domain", "test_service")
|
||||
config = {
|
||||
@@ -344,7 +344,7 @@ async def test_passing_variables_to_templates(hass: HomeAssistant):
|
||||
assert calls[0].data == {"hello": "goodbye", "entity_id": ["hello.world"]}
|
||||
|
||||
|
||||
async def test_bad_template(hass: HomeAssistant):
|
||||
async def test_bad_template(hass: HomeAssistant) -> None:
|
||||
"""Test passing bad template."""
|
||||
calls = async_mock_service(hass, "test_domain", "test_service")
|
||||
config = {
|
||||
@@ -366,7 +366,7 @@ async def test_bad_template(hass: HomeAssistant):
|
||||
assert len(calls) == 0
|
||||
|
||||
|
||||
async def test_split_entity_string(hass: HomeAssistant):
|
||||
async def test_split_entity_string(hass: HomeAssistant) -> None:
|
||||
"""Test splitting of entity string."""
|
||||
calls = async_mock_service(hass, "test_domain", "test_service")
|
||||
await service.async_call_from_config(
|
||||
@@ -380,7 +380,7 @@ async def test_split_entity_string(hass: HomeAssistant):
|
||||
assert ["hello.world", "sensor.beer"] == calls[-1].data.get("entity_id")
|
||||
|
||||
|
||||
async def test_not_mutate_input(hass: HomeAssistant):
|
||||
async def test_not_mutate_input(hass: HomeAssistant) -> None:
|
||||
"""Test for immutable input."""
|
||||
async_mock_service(hass, "test_domain", "test_service")
|
||||
config = {
|
||||
@@ -403,7 +403,7 @@ async def test_not_mutate_input(hass: HomeAssistant):
|
||||
|
||||
|
||||
@patch("homeassistant.helpers.service._LOGGER.error")
|
||||
async def test_fail_silently_if_no_service(mock_log, hass: HomeAssistant):
|
||||
async def test_fail_silently_if_no_service(mock_log, hass: HomeAssistant) -> None:
|
||||
"""Test failing if service is missing."""
|
||||
await service.async_call_from_config(hass, None)
|
||||
assert mock_log.call_count == 1
|
||||
|
@@ -75,7 +75,7 @@ def test_async_add_hass_job_schedule_callback() -> None:
|
||||
assert len(hass.add_job.mock_calls) == 0
|
||||
|
||||
|
||||
def test_async_add_hass_job_coro_named(hass) -> None:
|
||||
def test_async_add_hass_job_coro_named(hass: HomeAssistant) -> None:
|
||||
"""Test that we schedule coroutines and add jobs to the job pool with a name."""
|
||||
|
||||
async def mycoro():
|
||||
|
@@ -492,7 +492,9 @@ def test_representing_yaml_loaded_data(
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config_yaml", ["key: thing1\nkey: thing2"])
|
||||
def test_duplicate_key(caplog, try_both_loaders, mock_hass_config_yaml: None) -> None:
|
||||
def test_duplicate_key(
|
||||
caplog: pytest.LogCaptureFixture, try_both_loaders, mock_hass_config_yaml: None
|
||||
) -> None:
|
||||
"""Test duplicate dict keys."""
|
||||
load_yaml_config_file(YAML_CONFIG_FILE)
|
||||
assert "contains duplicate key" in caplog.text
|
||||
@@ -503,7 +505,7 @@ def test_duplicate_key(caplog, try_both_loaders, mock_hass_config_yaml: None) ->
|
||||
[{YAML_CONFIG_FILE: "key: !secret a", yaml.SECRET_YAML: "a: 1\nb: !secret a"}],
|
||||
)
|
||||
def test_no_recursive_secrets(
|
||||
caplog, try_both_loaders, mock_hass_config_yaml: None
|
||||
caplog: pytest.LogCaptureFixture, try_both_loaders, mock_hass_config_yaml: None
|
||||
) -> None:
|
||||
"""Test that loading of secrets from the secrets file fails correctly."""
|
||||
with pytest.raises(HomeAssistantError) as e:
|
||||
|
Reference in New Issue
Block a user