Merge branch 'dev' into webrtc

This commit is contained in:
J. Nick Koston
2023-05-23 08:27:36 -05:00
committed by GitHub
44 changed files with 1148 additions and 853 deletions
+1
View File
@@ -330,6 +330,7 @@ omit =
homeassistant/components/ezviz/entity.py
homeassistant/components/ezviz/sensor.py
homeassistant/components/ezviz/switch.py
homeassistant/components/ezviz/update.py
homeassistant/components/faa_delays/__init__.py
homeassistant/components/faa_delays/binary_sensor.py
homeassistant/components/familyhub/camera.py
+24 -3
View File
@@ -5,11 +5,16 @@ from typing import Any, Final
from aioairzone.common import OperationAction, OperationMode
from aioairzone.const import (
API_COOL_SET_POINT,
API_HEAT_SET_POINT,
API_MODE,
API_ON,
API_SET_POINT,
API_SPEED,
AZD_ACTION,
AZD_COOL_TEMP_SET,
AZD_DOUBLE_SET_POINT,
AZD_HEAT_TEMP_SET,
AZD_HUMIDITY,
AZD_MASTER,
AZD_MODE,
@@ -27,6 +32,8 @@ from aioairzone.const import (
)
from homeassistant.components.climate import (
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
FAN_AUTO,
FAN_HIGH,
FAN_LOW,
@@ -137,6 +144,10 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
and self.get_airzone_value(AZD_SPEEDS) is not None
):
self._set_fan_speeds()
if self.get_airzone_value(AZD_DOUBLE_SET_POINT):
self._attr_supported_features |= (
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
self._async_update_attrs()
@@ -201,9 +212,12 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
params = {
API_SET_POINT: kwargs.get(ATTR_TEMPERATURE),
}
params = {}
if ATTR_TEMPERATURE in kwargs:
params[API_SET_POINT] = kwargs[ATTR_TEMPERATURE]
if ATTR_TARGET_TEMP_LOW in kwargs and ATTR_TARGET_TEMP_HIGH in kwargs:
params[API_COOL_SET_POINT] = kwargs[ATTR_TARGET_TEMP_LOW]
params[API_HEAT_SET_POINT] = kwargs[ATTR_TARGET_TEMP_HIGH]
await self._async_update_hvac_params(params)
@callback
@@ -229,3 +243,10 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
self._attr_target_temperature = self.get_airzone_value(AZD_TEMP_SET)
if self.supported_features & ClimateEntityFeature.FAN_MODE:
self._attr_fan_mode = self._speeds.get(self.get_airzone_value(AZD_SPEED))
if self.supported_features & ClimateEntityFeature.TARGET_TEMPERATURE_RANGE:
self._attr_target_temperature_high = self.get_airzone_value(
AZD_HEAT_TEMP_SET
)
self._attr_target_temperature_low = self.get_airzone_value(
AZD_COOL_TEMP_SET
)
@@ -44,6 +44,7 @@ PLATFORMS = [
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
]
SERVICE_UPDATE_STATE = "update_state"
@@ -53,12 +53,6 @@ BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
name="Activate air conditioning",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning(),
),
BMWButtonEntityDescription(
key="deactivate_air_conditioning",
icon="mdi:hvac-off",
name="Deactivate air conditioning",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning_stop(),
),
BMWButtonEntityDescription(
key="find_vehicle",
icon="mdi:crosshairs-question",
@@ -0,0 +1,109 @@
"""Switch platform for BMW."""
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
import logging
from typing import Any
from bimmer_connected.models import MyBMWAPIError
from bimmer_connected.vehicle import MyBMWVehicle
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BMWBaseEntity
from .const import DOMAIN
from .coordinator import BMWDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
@dataclass
class BMWRequiredKeysMixin:
"""Mixin for required keys."""
value_fn: Callable[[MyBMWVehicle], bool]
remote_service_on: Callable[[MyBMWVehicle], Coroutine[Any, Any, Any]]
remote_service_off: Callable[[MyBMWVehicle], Coroutine[Any, Any, Any]]
@dataclass
class BMWSwitchEntityDescription(SwitchEntityDescription, BMWRequiredKeysMixin):
"""Describes BMW switch entity."""
is_available: Callable[[MyBMWVehicle], bool] = lambda _: False
dynamic_options: Callable[[MyBMWVehicle], list[str]] | None = None
NUMBER_TYPES: list[BMWSwitchEntityDescription] = [
BMWSwitchEntityDescription(
key="climate",
name="Climate",
is_available=lambda v: v.is_remote_climate_stop_enabled,
value_fn=lambda v: v.climate.is_climate_on,
remote_service_on=lambda v: v.remote_services.trigger_remote_air_conditioning(),
remote_service_off=lambda v: v.remote_services.trigger_remote_air_conditioning_stop(),
icon="mdi:fan",
),
]
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the MyBMW switch from config entry."""
coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
entities: list[BMWSwitch] = []
for vehicle in coordinator.account.vehicles:
if not coordinator.read_only:
entities.extend(
[
BMWSwitch(coordinator, vehicle, description)
for description in NUMBER_TYPES
if description.is_available(vehicle)
]
)
async_add_entities(entities)
class BMWSwitch(BMWBaseEntity, SwitchEntity):
"""Representation of BMW Switch entity."""
entity_description: BMWSwitchEntityDescription
def __init__(
self,
coordinator: BMWDataUpdateCoordinator,
vehicle: MyBMWVehicle,
description: BMWSwitchEntityDescription,
) -> None:
"""Initialize an BMW Switch."""
super().__init__(coordinator, vehicle)
self.entity_description = description
self._attr_unique_id = f"{vehicle.vin}-{description.key}"
@property
def is_on(self) -> bool:
"""Return the entity value to represent the entity state."""
return self.entity_description.value_fn(self.vehicle)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
try:
await self.entity_description.remote_service_on(self.vehicle)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
try:
await self.entity_description.remote_service_off(self.vehicle)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex
@@ -41,8 +41,9 @@ async def async_setup(hass):
hass.http.register_view(OptionManagerFlowIndexView(hass.config_entries.options))
hass.http.register_view(OptionManagerFlowResourceView(hass.config_entries.options))
websocket_api.async_register_command(hass, config_entries_get)
websocket_api.async_register_command(hass, config_entries_get_matching)
websocket_api.async_register_command(hass, config_entry_disable)
websocket_api.async_register_command(hass, config_entry_get)
websocket_api.async_register_command(hass, config_entry_update)
websocket_api.async_register_command(hass, config_entries_subscribe)
websocket_api.async_register_command(hass, config_entries_progress)
@@ -284,6 +285,28 @@ def get_entry(
return entry
@websocket_api.require_admin
@websocket_api.websocket_command(
{
"type": "config_entries/get",
"entry_id": str,
}
)
@websocket_api.async_response
async def config_entry_get(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Update config entry."""
entry = get_entry(hass, connection, msg["entry_id"], msg["id"])
if entry is None:
return
result = {"config_entry": entry_json(entry)}
connection.send_result(msg["id"], result)
@websocket_api.require_admin
@websocket_api.websocket_command(
{
@@ -409,13 +432,13 @@ async def ignore_config_flow(
@websocket_api.websocket_command(
{
vol.Required("type"): "config_entries/get",
vol.Required("type"): "config_entries/get_matching",
vol.Optional("type_filter"): vol.All(cv.ensure_list, [str]),
vol.Optional("domain"): str,
}
)
@websocket_api.async_response
async def config_entries_get(
async def config_entries_get_matching(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
@@ -37,6 +37,7 @@ PLATFORMS_BY_TYPE: dict[str, list] = {
Platform.CAMERA,
Platform.SENSOR,
Platform.SWITCH,
Platform.UPDATE,
],
}
@@ -25,10 +25,6 @@ BINARY_SENSOR_TYPES: dict[str, BinarySensorEntityDescription] = {
key="alarm_schedules_enabled"
),
"encrypted": BinarySensorEntityDescription(key="encrypted"),
"upgrade_available": BinarySensorEntityDescription(
key="upgrade_available",
device_class=BinarySensorDeviceClass.UPDATE,
),
}
+1 -1
View File
@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/ezviz",
"iot_class": "cloud_polling",
"loggers": ["paho_mqtt", "pyezviz"],
"requirements": ["pyezviz==0.2.0.9"]
"requirements": ["pyezviz==0.2.0.12"]
}
+109
View File
@@ -0,0 +1,109 @@
"""Support for EZVIZ sensors."""
from __future__ import annotations
from typing import Any
from pyezviz import HTTPError, PyEzvizError
from homeassistant.components.update import (
UpdateDeviceClass,
UpdateEntity,
UpdateEntityDescription,
UpdateEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DATA_COORDINATOR, DOMAIN
from .coordinator import EzvizDataUpdateCoordinator
from .entity import EzvizEntity
PARALLEL_UPDATES = 1
UPDATE_ENTITY_TYPES = UpdateEntityDescription(
key="version",
name="Firmware update",
device_class=UpdateDeviceClass.FIRMWARE,
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up EZVIZ sensors based on a config entry."""
coordinator: EzvizDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
DATA_COORDINATOR
]
async_add_entities(
EzvizUpdateEntity(coordinator, camera, sensor, UPDATE_ENTITY_TYPES)
for camera in coordinator.data
for sensor, value in coordinator.data[camera].items()
if sensor in UPDATE_ENTITY_TYPES.key
if value
)
class EzvizUpdateEntity(EzvizEntity, UpdateEntity):
"""Representation of a EZVIZ Update entity."""
_attr_has_entity_name = True
_attr_supported_features = (
UpdateEntityFeature.INSTALL
| UpdateEntityFeature.PROGRESS
| UpdateEntityFeature.RELEASE_NOTES
)
def __init__(
self,
coordinator: EzvizDataUpdateCoordinator,
serial: str,
sensor: str,
description: UpdateEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator, serial)
self._attr_unique_id = f"{serial}_{sensor}"
self.entity_description = description
@property
def installed_version(self) -> str | None:
"""Version installed and in use."""
return self.data["version"]
@property
def in_progress(self) -> bool | int | None:
"""Update installation progress."""
if self.data["upgrade_in_progress"]:
return self.data["upgrade_percent"]
return False
@property
def latest_version(self) -> str | None:
"""Latest version available for install."""
if self.data["upgrade_available"]:
return self.data["latest_firmware_info"]["version"]
return self.installed_version
def release_notes(self) -> str | None:
"""Return full release notes."""
if self.data["latest_firmware_info"]:
return self.data["latest_firmware_info"].get("desc")
return None
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
"""Install an update."""
try:
await self.hass.async_add_executor_job(
self.coordinator.ezviz_client.upgrade_device, self._serial
)
except (HTTPError, PyEzvizError) as err:
raise HomeAssistantError(
f"Failed to update firmware on {self.name}"
) from err
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/glances",
"iot_class": "local_polling",
"loggers": ["glances_api"],
"requirements": ["glances_api==0.4.1"]
"requirements": ["glances_api==0.4.2"]
}
+45 -25
View File
@@ -197,25 +197,23 @@ class IntegrationSensor(RestoreEntity, SensorEntity):
old_state: State | None = event.data.get("old_state")
new_state: State | None = event.data.get("new_state")
if (
source_state := self.hass.states.get(self._sensor_source_id)
) is None or source_state.state == STATE_UNAVAILABLE:
self._attr_available = False
self.async_write_ha_state()
return
self._attr_available = True
if new_state is None or new_state.state in (
STATE_UNKNOWN,
STATE_UNAVAILABLE,
):
return
# We may want to update our state before an early return,
# based on the source sensor's unit_of_measurement
# or device_class.
update_state = False
if (
source_state := self.hass.states.get(self._sensor_source_id)
) is None or source_state.state == STATE_UNAVAILABLE:
self._attr_available = False
update_state = True
else:
self._attr_available = True
if old_state is None or new_state is None:
# we can't calculate the elapsed time, so we can't calculate the integral
return
unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
if unit is not None:
new_unit_of_measurement = self._unit(unit)
@@ -235,31 +233,53 @@ class IntegrationSensor(RestoreEntity, SensorEntity):
if update_state:
self.async_write_ha_state()
if old_state is None or old_state.state in (
STATE_UNKNOWN,
STATE_UNAVAILABLE,
):
return
try:
# integration as the Riemann integral of previous measures.
area = Decimal(0)
elapsed_time = (
new_state.last_updated - old_state.last_updated
).total_seconds()
if self._method == METHOD_TRAPEZOIDAL:
if (
self._method == METHOD_TRAPEZOIDAL
and new_state.state
not in (
STATE_UNKNOWN,
STATE_UNAVAILABLE,
)
and old_state.state
not in (
STATE_UNKNOWN,
STATE_UNAVAILABLE,
)
):
area = (
(Decimal(new_state.state) + Decimal(old_state.state))
* Decimal(elapsed_time)
/ 2
)
elif self._method == METHOD_LEFT:
elif self._method == METHOD_LEFT and old_state.state not in (
STATE_UNKNOWN,
STATE_UNAVAILABLE,
):
area = Decimal(old_state.state) * Decimal(elapsed_time)
elif self._method == METHOD_RIGHT:
elif self._method == METHOD_RIGHT and new_state.state not in (
STATE_UNKNOWN,
STATE_UNAVAILABLE,
):
area = Decimal(new_state.state) * Decimal(elapsed_time)
else:
_LOGGER.debug(
"Could not apply method %s to %s -> %s",
self._method,
old_state.state,
new_state.state,
)
return
integral = area / (self._unit_prefix * self._unit_time)
_LOGGER.debug(
"area = %s, integral = %s state = %s", area, integral, self._state
)
assert isinstance(integral, Decimal)
except ValueError as err:
_LOGGER.warning("While calculating integration: %s", err)
@@ -12,6 +12,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .helper import get_hostname_id
_LOGGER = logging.getLogger(__name__)
@@ -32,9 +33,10 @@ async def test_connection(hass: HomeAssistant, data) -> str:
session = async_get_clientsession(hass)
async with ApiClient(session, data["host"]) as client:
await client.login(data["password"])
values = await client.get_setting_values("scb:network", "Hostname")
hostname_id = await get_hostname_id(client)
values = await client.get_setting_values("scb:network", hostname_id)
return values["scb:network"]["Hostname"]
return values["scb:network"][hostname_id]
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@@ -23,6 +23,7 @@ from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
_DataT = TypeVar("_DataT")
_KNOWN_HOSTNAME_IDS = ("Network:Hostname", "Hostname")
class Plenticore:
@@ -69,6 +70,7 @@ class Plenticore:
)
# get some device meta data
hostname_id = await get_hostname_id(self._client)
settings = await self._client.get_setting_values(
{
"devices:local": [
@@ -78,7 +80,7 @@ class Plenticore:
"Properties:VersionIOC",
"Properties:VersionMC",
],
"scb:network": ["Hostname"],
"scb:network": [hostname_id],
}
)
@@ -91,7 +93,7 @@ class Plenticore:
identifiers={(DOMAIN, device_local["Properties:SerialNo"])},
manufacturer="Kostal",
model=f"{prod1} {prod2}",
name=settings["scb:network"]["Hostname"],
name=settings["scb:network"][hostname_id],
sw_version=f'IOC: {device_local["Properties:VersionIOC"]}'
+ f' MC: {device_local["Properties:VersionMC"]}',
)
@@ -403,3 +405,12 @@ class PlenticoreDataFormatter:
return state
return PlenticoreDataFormatter.EM_STATES.get(value)
async def get_hostname_id(client: ApiClient) -> str:
"""Check for known existing hostname ids."""
all_settings = await client.get_settings()
for entry in all_settings["scb:network"]:
if entry.id in _KNOWN_HOSTNAME_IDS:
return entry.id
raise ApiException("Hostname identifier not found in KNOWN_HOSTNAME_IDS")
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/mystrom",
"iot_class": "local_polling",
"loggers": ["pymystrom"],
"requirements": ["python-mystrom==1.1.2"]
"requirements": ["python-mystrom==2.2.0"]
}
@@ -152,7 +152,7 @@ class PlugwiseBinarySensorEntity(PlugwiseEntity, BinarySensorEntity):
self._attr_unique_id = f"{device_id}-{description.key}"
@property
def is_on(self) -> bool | None:
def is_on(self) -> bool:
"""Return true if the binary sensor is on."""
return self.entity_description.value_fn(self.device["binary_sensors"])
+34 -9
View File
@@ -1,8 +1,12 @@
"""Plugwise Switch component for HomeAssistant."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any
from plugwise import SmileSwitches
from homeassistant.components.switch import (
SwitchDeviceClass,
SwitchEntity,
@@ -18,29 +22,46 @@ from .coordinator import PlugwiseDataUpdateCoordinator
from .entity import PlugwiseEntity
from .util import plugwise_command
SWITCHES: tuple[SwitchEntityDescription, ...] = (
SwitchEntityDescription(
@dataclass
class PlugwiseSwitchBaseMixin:
"""Mixin for required Plugwise switch description keys."""
value_fn: Callable[[SmileSwitches], bool]
@dataclass
class PlugwiseSwitchEntityDescription(SwitchEntityDescription, PlugwiseSwitchBaseMixin):
"""Describes Plugwise switch entity."""
SWITCHES: tuple[PlugwiseSwitchEntityDescription, ...] = (
PlugwiseSwitchEntityDescription(
key="dhw_cm_switch",
translation_key="dhw_cm_switch",
icon="mdi:water-plus",
entity_category=EntityCategory.CONFIG,
value_fn=lambda data: data["dhw_cm_switch"],
),
SwitchEntityDescription(
PlugwiseSwitchEntityDescription(
key="lock",
translation_key="lock",
icon="mdi:lock",
entity_category=EntityCategory.CONFIG,
value_fn=lambda data: data["lock"],
),
SwitchEntityDescription(
PlugwiseSwitchEntityDescription(
key="relay",
translation_key="relay",
device_class=SwitchDeviceClass.SWITCH,
value_fn=lambda data: data["relay"],
),
SwitchEntityDescription(
PlugwiseSwitchEntityDescription(
key="cooling_ena_switch",
name="Cooling",
icon="mdi:snowflake-thermometer",
entity_category=EntityCategory.CONFIG,
value_fn=lambda data: data["cooling_ena_switch"],
),
)
@@ -54,8 +75,10 @@ async def async_setup_entry(
coordinator = hass.data[DOMAIN][config_entry.entry_id]
entities: list[PlugwiseSwitchEntity] = []
for device_id, device in coordinator.data.devices.items():
if not (switches := device.get("switches")):
continue
for description in SWITCHES:
if "switches" not in device or description.key not in device["switches"]:
if description.key not in switches:
continue
entities.append(PlugwiseSwitchEntity(coordinator, device_id, description))
async_add_entities(entities)
@@ -64,11 +87,13 @@ async def async_setup_entry(
class PlugwiseSwitchEntity(PlugwiseEntity, SwitchEntity):
"""Representation of a Plugwise plug."""
entity_description: PlugwiseSwitchEntityDescription
def __init__(
self,
coordinator: PlugwiseDataUpdateCoordinator,
device_id: str,
description: SwitchEntityDescription,
description: PlugwiseSwitchEntityDescription,
) -> None:
"""Set up the Plugwise API."""
super().__init__(coordinator, device_id)
@@ -76,9 +101,9 @@ class PlugwiseSwitchEntity(PlugwiseEntity, SwitchEntity):
self._attr_unique_id = f"{device_id}-{description.key}"
@property
def is_on(self) -> bool | None:
def is_on(self) -> bool:
"""Return True if entity is on."""
return self.device["switches"].get(self.entity_description.key)
return self.entity_description.value_fn(self.device["switches"])
@plugwise_command
async def async_turn_on(self, **kwargs: Any) -> None:
+2 -73
View File
@@ -3,14 +3,11 @@ from __future__ import annotations
from collections.abc import Coroutine, Mapping
from functools import partial
import socket
from typing import Any
from urllib.parse import urlparse
import getmac
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import ssdp
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@@ -18,7 +15,6 @@ from homeassistant.const import (
CONF_MAC,
CONF_METHOD,
CONF_MODEL,
CONF_NAME,
CONF_PORT,
CONF_TOKEN,
EVENT_HOMEASSISTANT_STOP,
@@ -30,10 +26,8 @@ from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
issue_registry as ir,
)
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.typing import ConfigType
from .bridge import (
SamsungTVBridge,
@@ -42,11 +36,9 @@ from .bridge import (
model_requires_encryption,
)
from .const import (
CONF_ON_ACTION,
CONF_SESSION_ID,
CONF_SSDP_MAIN_TV_AGENT_LOCATION,
CONF_SSDP_RENDERING_CONTROL_LOCATION,
DEFAULT_NAME,
DOMAIN,
ENTRY_RELOAD_COOLDOWN,
LEGACY_PORT,
@@ -57,73 +49,9 @@ from .const import (
UPNP_SVC_RENDERING_CONTROL,
)
def ensure_unique_hosts(value: dict[Any, Any]) -> dict[Any, Any]:
"""Validate that all configs have a unique host."""
vol.Schema(vol.Unique("duplicate host entries found"))(
[entry[CONF_HOST] for entry in value]
)
return value
PLATFORMS = [Platform.MEDIA_PLAYER]
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.All(
cv.ensure_list,
[
cv.deprecated(CONF_PORT),
vol.Schema(
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT): cv.port,
vol.Optional(CONF_ON_ACTION): cv.SCRIPT_SCHEMA,
}
),
],
ensure_unique_hosts,
)
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Samsung TV integration."""
hass.data[DOMAIN] = {}
if DOMAIN not in config:
return True
ir.async_create_issue(
hass,
DOMAIN,
"deprecated_yaml",
breaks_in_ha_version="2023.6.0",
is_fixable=False,
severity=ir.IssueSeverity.WARNING,
translation_key="deprecated_yaml",
translation_placeholders={
"on_action_url": "https://www.home-assistant.io/integrations/samsungtv/#turn-on-action"
},
learn_more_url="https://www.home-assistant.io/integrations/samsungtv/#turn-on-action",
)
for entry_config in config[DOMAIN]:
ip_address = await hass.async_add_executor_job(
socket.gethostbyname, entry_config[CONF_HOST]
)
hass.data[DOMAIN][ip_address] = {
CONF_ON_ACTION: entry_config.get(CONF_ON_ACTION)
}
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=entry_config,
)
)
return True
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
@callback
@@ -195,6 +123,7 @@ async def _async_update_ssdp_locations(hass: HomeAssistant, entry: ConfigEntry)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the Samsung TV platform."""
hass.data.setdefault(DOMAIN, {})
# Initialize bridge
if entry.data.get(CONF_METHOD) == METHOD_ENCRYPTED_WEBSOCKET:
@@ -35,12 +35,9 @@ from .const import (
CONF_SSDP_RENDERING_CONTROL_LOCATION,
DEFAULT_MANUFACTURER,
DOMAIN,
ENCRYPTED_WEBSOCKET_PORT,
LEGACY_PORT,
LOGGER,
METHOD_ENCRYPTED_WEBSOCKET,
METHOD_LEGACY,
METHOD_WEBSOCKET,
RESULT_AUTH_MISSING,
RESULT_CANNOT_CONNECT,
RESULT_INVALID_PIN,
@@ -50,7 +47,6 @@ from .const import (
SUCCESSFUL_RESULTS,
UPNP_SVC_MAIN_TV_AGENT,
UPNP_SVC_RENDERING_CONTROL,
WEBSOCKET_PORTS,
)
DATA_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str, vol.Required(CONF_NAME): str})
@@ -231,25 +227,6 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self._mac = mac
return True
async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult:
"""Handle configuration by yaml file."""
# We need to import even if we cannot validate
# since the TV may be off at startup
await self._async_set_name_host_from_input(user_input)
self._async_abort_entries_match({CONF_HOST: self._host})
port = user_input.get(CONF_PORT)
if port in WEBSOCKET_PORTS:
user_input[CONF_METHOD] = METHOD_WEBSOCKET
elif port == ENCRYPTED_WEBSOCKET_PORT:
user_input[CONF_METHOD] = METHOD_ENCRYPTED_WEBSOCKET
elif port == LEGACY_PORT:
user_input[CONF_METHOD] = METHOD_LEGACY
user_input[CONF_MANUFACTURER] = DEFAULT_MANUFACTURER
return self.async_create_entry(
title=self._title,
data=user_input,
)
async def _async_set_name_host_from_input(self, user_input: dict[str, Any]) -> None:
try:
self._host = await self.hass.async_add_executor_job(
@@ -6,7 +6,6 @@ DOMAIN = "samsungtv"
ATTR_PROPERTIES = "properties"
DEFAULT_NAME = "Samsung TV"
DEFAULT_MANUFACTURER = "Samsung"
VALUE_CONF_NAME = "HomeAssistant"
@@ -16,7 +15,6 @@ CONF_DESCRIPTION = "description"
CONF_MANUFACTURER = "manufacturer"
CONF_SSDP_RENDERING_CONTROL_LOCATION = "ssdp_rendering_control_location"
CONF_SSDP_MAIN_TV_AGENT_LOCATION = "ssdp_main_tv_agent_location"
CONF_ON_ACTION = "turn_on_action"
CONF_SESSION_ID = "session_id"
RESULT_AUTH_MISSING = "auth_missing"
@@ -41,16 +41,13 @@ from homeassistant.helpers import (
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.script import Script
from homeassistant.helpers.trigger import PluggableAction
from homeassistant.util import dt as dt_util
from .bridge import SamsungTVBridge, SamsungTVWSBridge
from .const import (
CONF_MANUFACTURER,
CONF_ON_ACTION,
CONF_SSDP_RENDERING_CONTROL_LOCATION,
DEFAULT_NAME,
DOMAIN,
LOGGER,
)
@@ -86,16 +83,7 @@ async def async_setup_entry(
) -> None:
"""Set up the Samsung TV from a config entry."""
bridge = hass.data[DOMAIN][entry.entry_id]
host = entry.data[CONF_HOST]
on_script = None
data = hass.data[DOMAIN]
if turn_on_action := data.get(host, {}).get(CONF_ON_ACTION):
on_script = Script(
hass, turn_on_action, entry.data.get(CONF_NAME, DEFAULT_NAME), DOMAIN
)
async_add_entities([SamsungTVDevice(bridge, entry, on_script)], True)
async_add_entities([SamsungTVDevice(bridge, entry)], True)
class SamsungTVDevice(MediaPlayerEntity):
@@ -107,7 +95,6 @@ class SamsungTVDevice(MediaPlayerEntity):
self,
bridge: SamsungTVBridge,
config_entry: ConfigEntry,
on_script: Script | None,
) -> None:
"""Initialize the Samsung device."""
self._config_entry = config_entry
@@ -117,7 +104,6 @@ class SamsungTVDevice(MediaPlayerEntity):
CONF_SSDP_RENDERING_CONTROL_LOCATION
)
self._turn_on = PluggableAction(self.async_write_ha_state)
self._on_script = on_script
# Assume that the TV is in Play mode
self._playing: bool = True
@@ -130,8 +116,8 @@ class SamsungTVDevice(MediaPlayerEntity):
self._app_list_event: asyncio.Event = asyncio.Event()
self._attr_supported_features = SUPPORT_SAMSUNGTV
if self._on_script or self._mac:
# (deprecated) add turn-on if on_script YAML or mac is available
if self._mac:
# (deprecated) add turn-on if mac is available
# Triggers have not yet been registered so this is adjusted in the property
self._attr_supported_features |= MediaPlayerEntityFeature.TURN_ON
if self._ssdp_rendering_control_location:
@@ -375,7 +361,6 @@ class SamsungTVDevice(MediaPlayerEntity):
return (
self.state == MediaPlayerState.ON
or bool(self._turn_on)
or self._on_script is not None
or self._mac is not None
or self._power_off_in_progress()
)
@@ -477,9 +462,6 @@ class SamsungTVDevice(MediaPlayerEntity):
"""Turn the media player on."""
if self._turn_on:
await self._turn_on.async_run(self.hass, self._context)
elif self._on_script:
# YAML on_script is deprecated - replaced by turn_on trigger
await self._on_script.async_run(context=self._context)
elif self._mac:
await self.hass.async_add_executor_job(self._wake_on_lan)
@@ -44,11 +44,5 @@
"trigger_type": {
"samsungtv.turn_on": "Device is requested to turn on"
}
},
"issues": {
"deprecated_yaml": {
"title": "The SamsungTV YAML configuration is being removed",
"description": "Configuring SamsungTV using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the SamsungTV YAML configuration from your `configuration.yaml` file and restart Home Assistant to fix this issue.\n\nPlease note that previously configured `turn_on_action` needs to be manually converted to use the `turn_on` trigger ([documentation]({on_action_url}))."
}
}
}
@@ -4,7 +4,7 @@
"codeowners": ["@luar123"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/snapcast",
"iot_class": "local_polling",
"iot_class": "local_push",
"loggers": ["construct", "snapcast"],
"requirements": ["snapcast==2.3.3"]
}
+1 -1
View File
@@ -5097,7 +5097,7 @@
"name": "Snapcast",
"integration_type": "hub",
"config_flow": true,
"iot_class": "local_polling"
"iot_class": "local_push"
},
"snips": {
"name": "Snips",
+1 -1
View File
@@ -156,7 +156,7 @@ pyOpenSSL>=23.1.0
# protobuf must be in package constraints for the wheel
# builder to build binary wheels
protobuf==4.22.3
protobuf==4.23.1
# faust-cchardet: Ensure we have a version we can build wheels
# 2.1.18 is the first version that works with our wheel builder
+3 -3
View File
@@ -802,7 +802,7 @@ gios==3.1.0
gitterpy==0.1.7
# homeassistant.components.glances
glances_api==0.4.1
glances_api==0.4.2
# homeassistant.components.goalzero
goalzero==0.2.1
@@ -1629,7 +1629,7 @@ pyeverlights==0.1.0
pyevilgenius==2.0.0
# homeassistant.components.ezviz
pyezviz==0.2.0.9
pyezviz==0.2.0.12
# homeassistant.components.fibaro
pyfibaro==0.7.1
@@ -2098,7 +2098,7 @@ python-miio==0.5.12
python-mpd2==3.0.5
# homeassistant.components.mystrom
python-mystrom==1.1.2
python-mystrom==2.2.0
# homeassistant.components.nest
python-nest==4.2.0
+2 -2
View File
@@ -624,7 +624,7 @@ getmac==0.8.2
gios==3.1.0
# homeassistant.components.glances
glances_api==0.4.1
glances_api==0.4.2
# homeassistant.components.goalzero
goalzero==0.2.1
@@ -1190,7 +1190,7 @@ pyeverlights==0.1.0
pyevilgenius==2.0.0
# homeassistant.components.ezviz
pyezviz==0.2.0.9
pyezviz==0.2.0.12
# homeassistant.components.fibaro
pyfibaro==0.7.1
+1 -1
View File
@@ -158,7 +158,7 @@ pyOpenSSL>=23.1.0
# protobuf must be in package constraints for the wheel
# builder to build binary wheels
protobuf==4.22.3
protobuf==4.23.1
# faust-cchardet: Ensure we have a version we can build wheels
# 2.1.18 is the first version that works with our wheel builder
+40
View File
@@ -3,7 +3,9 @@ from unittest.mock import patch
from aioairzone.common import OperationMode
from aioairzone.const import (
API_COOL_SET_POINT,
API_DATA,
API_HEAT_SET_POINT,
API_MODE,
API_ON,
API_SET_POINT,
@@ -25,6 +27,8 @@ from homeassistant.components.climate import (
ATTR_HVAC_MODES,
ATTR_MAX_TEMP,
ATTR_MIN_TEMP,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
ATTR_TARGET_TEMP_STEP,
DOMAIN as CLIMATE_DOMAIN,
FAN_AUTO,
@@ -494,3 +498,39 @@ async def test_airzone_climate_set_temp_error(hass: HomeAssistant) -> None:
state = hass.states.get("climate.dorm_2")
assert state.attributes.get(ATTR_TEMPERATURE) == 19.5
async def test_airzone_climate_set_temp_range(hass: HomeAssistant) -> None:
"""Test setting the target temperature range."""
HVAC_MOCK = {
API_DATA: [
{
API_SYSTEM_ID: 3,
API_ZONE_ID: 1,
API_COOL_SET_POINT: 68.0,
API_HEAT_SET_POINT: 77.0,
}
]
}
await async_init_integration(hass)
with patch(
"homeassistant.components.airzone.AirzoneLocalApi.put_hvac",
return_value=HVAC_MOCK,
):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: "climate.dkn_plus",
ATTR_TARGET_TEMP_HIGH: 25.0,
ATTR_TARGET_TEMP_LOW: 20.0,
},
blocking=True,
)
state = hass.states.get("climate.dkn_plus")
assert state.attributes.get(ATTR_TARGET_TEMP_HIGH) == 25.0
assert state.attributes.get(ATTR_TARGET_TEMP_LOW) == 20.0
@@ -36,7 +36,7 @@ async def test_options_flow_disabled_not_setup(
await ws_client.send_json(
{
"id": 5,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"domain": "bluetooth",
}
)
@@ -370,7 +370,7 @@ async def test_options_flow_disabled_macos(
await ws_client.send_json(
{
"id": 5,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"domain": "bluetooth",
}
)
@@ -403,7 +403,7 @@ async def test_options_flow_enabled_linux(
await ws_client.send_json(
{
"id": 5,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"domain": "bluetooth",
}
)
@@ -0,0 +1,17 @@
# serializer version: 1
# name: test_entity_state_attrs
list([
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by MyBMW',
'friendly_name': 'i4 eDrive40 Climate',
'icon': 'mdi:fan',
}),
'context': <ANY>,
'entity_id': 'switch.i4_edrive40_climate',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'off',
}),
])
# ---
@@ -0,0 +1,100 @@
"""Test BMW switches."""
from unittest.mock import AsyncMock
from bimmer_connected.models import MyBMWAPIError, MyBMWRemoteServiceError
from bimmer_connected.vehicle.remote_services import RemoteServices
import pytest
import respx
from syrupy.assertion import SnapshotAssertion
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from . import setup_mocked_integration
async def test_entity_state_attrs(
hass: HomeAssistant,
bmw_fixture: respx.Router,
snapshot: SnapshotAssertion,
) -> None:
"""Test switch options and values.."""
# Setup component
assert await setup_mocked_integration(hass)
# Get all switch entities
assert hass.states.async_all("switch") == snapshot
@pytest.mark.parametrize(
("entity_id", "value"),
[
("switch.i4_edrive40_climate", "ON"),
("switch.i4_edrive40_climate", "OFF"),
],
)
async def test_update_triggers_success(
hass: HomeAssistant,
entity_id: str,
value: str,
bmw_fixture: respx.Router,
) -> None:
"""Test allowed values for switch inputs."""
# Setup component
assert await setup_mocked_integration(hass)
# Test
await hass.services.async_call(
"switch",
f"turn_{value.lower()}",
blocking=True,
target={"entity_id": entity_id},
)
assert RemoteServices.trigger_remote_service.call_count == 1
@pytest.mark.parametrize(
("raised", "expected"),
[
(MyBMWRemoteServiceError, HomeAssistantError),
(MyBMWAPIError, HomeAssistantError),
(ValueError, ValueError),
],
)
async def test_update_triggers_exceptions(
hass: HomeAssistant,
raised: Exception,
expected: Exception,
bmw_fixture: respx.Router,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test not allowed values for switch inputs."""
# Setup component
assert await setup_mocked_integration(hass)
# Setup exception
monkeypatch.setattr(
RemoteServices,
"trigger_remote_service",
AsyncMock(side_effect=raised),
)
# Test
with pytest.raises(expected):
await hass.services.async_call(
"switch",
"turn_on",
blocking=True,
target={"entity_id": "switch.i4_edrive40_climate"},
)
with pytest.raises(expected):
await hass.services.async_call(
"switch",
"turn_off",
blocking=True,
target={"entity_id": "switch.i4_edrive40_climate"},
)
assert RemoteServices.trigger_remote_service.call_count == 2
+63 -32
View File
@@ -965,6 +965,55 @@ async def test_options_flow_with_invalid_data(hass: HomeAssistant, client) -> No
}
async def test_get(hass: HomeAssistant, hass_ws_client: WebSocketGenerator) -> None:
"""Test that we can get a config entry."""
assert await async_setup_component(hass, "config", {})
ws_client = await hass_ws_client(hass)
entry = MockConfigEntry(domain="demo", state=core_ce.ConfigEntryState.LOADED)
entry.add_to_hass(hass)
assert entry.pref_disable_new_entities is False
assert entry.pref_disable_polling is False
await ws_client.send_json_auto_id(
{
"type": "config_entries/get",
"entry_id": entry.entry_id,
}
)
response = await ws_client.receive_json()
assert response["success"]
assert response["result"]["config_entry"] == {
"disabled_by": None,
"domain": "demo",
"entry_id": entry.entry_id,
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"reason": None,
"source": "user",
"state": "loaded",
"supports_options": False,
"supports_remove_device": False,
"supports_unload": False,
"title": "Mock Title",
}
await ws_client.send_json_auto_id(
{
"type": "config_entries/get",
"entry_id": "blah",
}
)
response = await ws_client.receive_json()
assert not response["success"]
assert response["error"] == {
"code": "not_found",
"message": "Config entry not found",
}
async def test_update_prefrences(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
@@ -1209,7 +1258,7 @@ async def test_ignore_flow_nonexisting(
assert response["error"]["code"] == "not_found"
async def test_get_entries_ws(
async def test_get_matching_entries_ws(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, clear_handlers
) -> None:
"""Test get entries with the websocket api."""
@@ -1260,14 +1309,8 @@ async def test_get_entries_ws(
ws_client = await hass_ws_client(hass)
await ws_client.send_json(
{
"id": 5,
"type": "config_entries/get",
}
)
await ws_client.send_json_auto_id({"type": "config_entries/get_matching"})
response = await ws_client.receive_json()
assert response["id"] == 5
assert response["result"] == [
{
"disabled_by": None,
@@ -1341,16 +1384,14 @@ async def test_get_entries_ws(
},
]
await ws_client.send_json(
await ws_client.send_json_auto_id(
{
"id": 6,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"domain": "comp1",
"type_filter": "hub",
}
)
response = await ws_client.receive_json()
assert response["id"] == 6
assert response["result"] == [
{
"disabled_by": None,
@@ -1368,15 +1409,13 @@ async def test_get_entries_ws(
}
]
await ws_client.send_json(
await ws_client.send_json_auto_id(
{
"id": 7,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"type_filter": ["service", "device"],
}
)
response = await ws_client.receive_json()
assert response["id"] == 7
assert response["result"] == [
{
"disabled_by": None,
@@ -1408,15 +1447,13 @@ async def test_get_entries_ws(
},
]
await ws_client.send_json(
await ws_client.send_json_auto_id(
{
"id": 8,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"type_filter": "hub",
}
)
response = await ws_client.receive_json()
assert response["id"] == 8
assert response["result"] == [
{
"disabled_by": None,
@@ -1453,16 +1490,14 @@ async def test_get_entries_ws(
"homeassistant.components.config.config_entries.async_get_integrations",
return_value={"any": IntegrationNotFound("any")},
):
await ws_client.send_json(
await ws_client.send_json_auto_id(
{
"id": 9,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"type_filter": "hub",
}
)
response = await ws_client.receive_json()
assert response["id"] == 9
assert response["result"] == [
{
"disabled_by": None,
@@ -1541,16 +1576,14 @@ async def test_get_entries_ws(
"homeassistant.components.config.config_entries.async_get_integrations",
return_value={"any": IntegrationNotFound("any")},
):
await ws_client.send_json(
await ws_client.send_json_auto_id(
{
"id": 10,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"type_filter": ["helper"],
}
)
response = await ws_client.receive_json()
assert response["id"] == 10
assert response["result"] == []
# Verify we raise if something really goes wrong
@@ -1559,16 +1592,14 @@ async def test_get_entries_ws(
"homeassistant.components.config.config_entries.async_get_integrations",
return_value={"any": Exception()},
):
await ws_client.send_json(
await ws_client.send_json_auto_id(
{
"id": 11,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"type_filter": ["device", "hub", "service"],
}
)
response = await ws_client.receive_json()
assert response["id"] == 11
assert response["success"] is False
+81 -10
View File
@@ -2,6 +2,8 @@
from datetime import timedelta
from unittest.mock import patch
import pytest
from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
@@ -20,7 +22,8 @@ import homeassistant.util.dt as dt_util
from tests.common import mock_restore_cache
async def test_state(hass: HomeAssistant) -> None:
@pytest.mark.parametrize("method", ["trapezoidal", "left", "right"])
async def test_state(hass: HomeAssistant, method) -> None:
"""Test integration sensor state."""
config = {
"sensor": {
@@ -28,6 +31,7 @@ async def test_state(hass: HomeAssistant) -> None:
"name": "integration",
"source": "sensor.power",
"round": 2,
"method": method,
}
}
@@ -46,8 +50,8 @@ async def test_state(hass: HomeAssistant) -> None:
assert state.attributes.get("state_class") is SensorStateClass.TOTAL
assert "device_class" not in state.attributes
future_now = dt_util.utcnow() + timedelta(seconds=3600)
with patch("homeassistant.util.dt.utcnow", return_value=future_now):
now += timedelta(seconds=3600)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.states.async_set(
entity_id,
1,
@@ -69,6 +73,62 @@ async def test_state(hass: HomeAssistant) -> None:
assert state.attributes.get("device_class") == SensorDeviceClass.ENERGY
assert state.attributes.get("state_class") is SensorStateClass.TOTAL
# 1 hour after last update, power sensor is unavailable
now += timedelta(seconds=3600)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.states.async_set(
entity_id,
STATE_UNAVAILABLE,
{
"device_class": SensorDeviceClass.POWER,
ATTR_UNIT_OF_MEASUREMENT: UnitOfPower.KILO_WATT,
},
force_update=True,
)
await hass.async_block_till_done()
state = hass.states.get("sensor.integration")
assert state.state == STATE_UNAVAILABLE
# 1 hour after last update, power sensor is back to normal at 2 KiloWatts and stays for 1 hour += 2kWh
now += timedelta(seconds=3600)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.states.async_set(
entity_id,
2,
{
"device_class": SensorDeviceClass.POWER,
ATTR_UNIT_OF_MEASUREMENT: UnitOfPower.KILO_WATT,
},
force_update=True,
)
await hass.async_block_till_done()
state = hass.states.get("sensor.integration")
assert (
round(float(state.state), config["sensor"]["round"]) == 3.0
if method == "right"
else 1.0
)
now += timedelta(seconds=3600)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.states.async_set(
entity_id,
2,
{
"device_class": SensorDeviceClass.POWER,
ATTR_UNIT_OF_MEASUREMENT: UnitOfPower.KILO_WATT,
},
force_update=True,
)
await hass.async_block_till_done()
state = hass.states.get("sensor.integration")
assert (
round(float(state.state), config["sensor"]["round"]) == 5.0
if method == "right"
else 3.0
)
async def test_restore_state(hass: HomeAssistant) -> None:
"""Test integration sensor state is restored correctly."""
@@ -416,13 +476,15 @@ async def test_units(hass: HomeAssistant) -> None:
assert new_state.state == STATE_UNAVAILABLE
async def test_device_class(hass: HomeAssistant) -> None:
@pytest.mark.parametrize("method", ["trapezoidal", "left", "right"])
async def test_device_class(hass: HomeAssistant, method) -> None:
"""Test integration sensor units using a power source."""
config = {
"sensor": {
"platform": "integration",
"name": "integration",
"source": "sensor.power",
"method": method,
}
}
@@ -465,13 +527,15 @@ async def test_device_class(hass: HomeAssistant) -> None:
assert state.attributes.get("device_class") == SensorDeviceClass.ENERGY
async def test_calc_errors(hass: HomeAssistant) -> None:
@pytest.mark.parametrize("method", ["trapezoidal", "left", "right"])
async def test_calc_errors(hass: HomeAssistant, method) -> None:
"""Test integration sensor units using a power source."""
config = {
"sensor": {
"platform": "integration",
"name": "integration",
"source": "sensor.power",
"method": method,
}
}
@@ -479,6 +543,7 @@ async def test_calc_errors(hass: HomeAssistant) -> None:
entity_id = config["sensor"]["source"]
now = dt_util.utcnow()
hass.states.async_set(entity_id, None, {})
await hass.async_block_till_done()
@@ -489,19 +554,25 @@ async def test_calc_errors(hass: HomeAssistant) -> None:
assert state.state == STATE_UNKNOWN
# Moving from an unknown state to a value is a calc error and should
# not change the value of the Reimann sensor.
hass.states.async_set(entity_id, 0, {"device_class": None})
# not change the value of the Reimann sensor, unless the method used is "right".
now += timedelta(seconds=3600)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.states.async_set(entity_id, 0, {"device_class": None})
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get("sensor.integration")
assert state is not None
assert state.state == STATE_UNKNOWN
assert state.state == STATE_UNKNOWN if method != "right" else "0.000"
# With the source sensor updated successfully, the Reimann sensor
# should have a zero (known) value.
hass.states.async_set(entity_id, 1, {"device_class": None})
now += timedelta(seconds=3600)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.states.async_set(entity_id, 1, {"device_class": None})
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get("sensor.integration")
assert state is not None
assert round(float(state.state)) == 0
assert round(float(state.state)) == 0 if method != "right" else 1
@@ -1,8 +1,10 @@
"""Test the Kostal Plenticore Solar Inverter config flow."""
import asyncio
from collections.abc import Generator
from unittest.mock import ANY, AsyncMock, MagicMock, patch
from pykoplenti import AuthenticationException
from pykoplenti import ApiClient, AuthenticationException, SettingsData
import pytest
from homeassistant import config_entries
from homeassistant.components.kostal_plenticore.const import DOMAIN
@@ -11,8 +13,33 @@ from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def test_formx(hass: HomeAssistant) -> None:
"""Test we get the form."""
@pytest.fixture
def mock_apiclient() -> ApiClient:
"""Return a mocked ApiClient instance."""
apiclient = MagicMock(spec=ApiClient)
apiclient.__aenter__.return_value = apiclient
apiclient.__aexit__ = AsyncMock()
return apiclient
@pytest.fixture
def mock_apiclient_class(mock_apiclient) -> Generator[type[ApiClient], None, None]:
"""Return a mocked ApiClient class."""
with patch(
"homeassistant.components.kostal_plenticore.config_flow.ApiClient",
autospec=True,
) as mock_api_class:
mock_api_class.return_value = mock_apiclient
yield mock_api_class
async def test_form_g1(
hass: HomeAssistant,
mock_apiclient_class: type[ApiClient],
mock_apiclient: ApiClient,
) -> None:
"""Test the config flow for G1 models."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@@ -21,25 +48,19 @@ async def test_formx(hass: HomeAssistant) -> None:
assert result["errors"] == {}
with patch(
"homeassistant.components.kostal_plenticore.config_flow.ApiClient"
) as mock_api_class, patch(
"homeassistant.components.kostal_plenticore.async_setup_entry",
return_value=True,
) as mock_setup_entry:
# mock of the context manager instance
mock_api_ctx = MagicMock()
mock_api_ctx.login = AsyncMock()
mock_api_ctx.get_setting_values = AsyncMock(
mock_apiclient.login = AsyncMock()
mock_apiclient.get_settings = AsyncMock(
return_value={"scb:network": [SettingsData({"id": "Hostname"})]}
)
mock_apiclient.get_setting_values = AsyncMock(
# G1 model has the entry id "Hostname"
return_value={"scb:network": {"Hostname": "scb"}}
)
# mock of the return instance of ApiClient
mock_api = MagicMock()
mock_api.__aenter__.return_value = mock_api_ctx
mock_api.__aexit__ = AsyncMock()
mock_api_class.return_value = mock_api
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
@@ -49,11 +70,68 @@ async def test_formx(hass: HomeAssistant) -> None:
)
await hass.async_block_till_done()
mock_api_class.assert_called_once_with(ANY, "1.1.1.1")
mock_api.__aenter__.assert_called_once()
mock_api.__aexit__.assert_called_once()
mock_api_ctx.login.assert_called_once_with("test-password")
mock_api_ctx.get_setting_values.assert_called_once()
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
mock_apiclient.__aenter__.assert_called_once()
mock_apiclient.__aexit__.assert_called_once()
mock_apiclient.login.assert_called_once_with("test-password")
mock_apiclient.get_settings.assert_called_once()
mock_apiclient.get_setting_values.assert_called_once_with(
"scb:network", "Hostname"
)
assert result2["type"] == "create_entry"
assert result2["title"] == "scb"
assert result2["data"] == {
"host": "1.1.1.1",
"password": "test-password",
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_g2(
hass: HomeAssistant,
mock_apiclient_class: type[ApiClient],
mock_apiclient: ApiClient,
) -> None:
"""Test the config flow for G2 models."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == "form"
assert result["errors"] == {}
with patch(
"homeassistant.components.kostal_plenticore.async_setup_entry",
return_value=True,
) as mock_setup_entry:
# mock of the context manager instance
mock_apiclient.login = AsyncMock()
mock_apiclient.get_settings = AsyncMock(
return_value={"scb:network": [SettingsData({"id": "Network:Hostname"})]}
)
mock_apiclient.get_setting_values = AsyncMock(
# G1 model has the entry id "Hostname"
return_value={"scb:network": {"Network:Hostname": "scb"}}
)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"host": "1.1.1.1",
"password": "test-password",
},
)
await hass.async_block_till_done()
mock_apiclient_class.assert_called_once_with(ANY, "1.1.1.1")
mock_apiclient.__aenter__.assert_called_once()
mock_apiclient.__aexit__.assert_called_once()
mock_apiclient.login.assert_called_once_with("test-password")
mock_apiclient.get_settings.assert_called_once()
mock_apiclient.get_setting_values.assert_called_once_with(
"scb:network", "Network:Hostname"
)
assert result2["type"] == "create_entry"
assert result2["title"] == "scb"
@@ -0,0 +1,107 @@
"""Test Kostal Plenticore helper."""
from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch
from pykoplenti import ApiClient, SettingsData
import pytest
from homeassistant.components.kostal_plenticore.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from tests.common import MockConfigEntry
@pytest.fixture
def mock_apiclient() -> Generator[ApiClient, None, None]:
"""Return a mocked ApiClient class."""
with patch(
"homeassistant.components.kostal_plenticore.helper.ApiClient",
autospec=True,
) as mock_api_class:
apiclient = MagicMock(spec=ApiClient)
apiclient.__aenter__.return_value = apiclient
apiclient.__aexit__ = AsyncMock()
mock_api_class.return_value = apiclient
yield apiclient
async def test_plenticore_async_setup_g1(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_apiclient: ApiClient,
) -> None:
"""Tests the async_setup() method of the Plenticore class for G1 models."""
mock_apiclient.get_settings = AsyncMock(
return_value={"scb:network": [SettingsData({"id": "Hostname"})]}
)
mock_apiclient.get_setting_values = AsyncMock(
# G1 model has the entry id "Hostname"
return_value={
"devices:local": {
"Properties:SerialNo": "12345",
"Branding:ProductName1": "PLENTICORE",
"Branding:ProductName2": "plus 10",
"Properties:VersionIOC": "01.45",
"Properties:VersionMC": "01.46",
},
"scb:network": {"Hostname": "scb"},
}
)
mock_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
plenticore = hass.data[DOMAIN][mock_config_entry.entry_id]
assert plenticore.device_info == DeviceInfo(
configuration_url="http://192.168.1.2",
identifiers={(DOMAIN, "12345")},
manufacturer="Kostal",
model="PLENTICORE plus 10",
name="scb",
sw_version="IOC: 01.45 MC: 01.46",
)
async def test_plenticore_async_setup_g2(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_apiclient: ApiClient,
) -> None:
"""Tests the async_setup() method of the Plenticore class for G2 models."""
mock_apiclient.get_settings = AsyncMock(
return_value={"scb:network": [SettingsData({"id": "Network:Hostname"})]}
)
mock_apiclient.get_setting_values = AsyncMock(
# G1 model has the entry id "Hostname"
return_value={
"devices:local": {
"Properties:SerialNo": "12345",
"Branding:ProductName1": "PLENTICORE",
"Branding:ProductName2": "plus 10",
"Properties:VersionIOC": "01.45",
"Properties:VersionMC": "01.46",
},
"scb:network": {"Network:Hostname": "scb"},
}
)
mock_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
plenticore = hass.data[DOMAIN][mock_config_entry.entry_id]
assert plenticore.device_info == DeviceInfo(
configuration_url="http://192.168.1.2",
identifiers={(DOMAIN, "12345")},
manufacturer="Kostal",
model="PLENTICORE plus 10",
name="scb",
sw_version="IOC: 01.45 MC: 01.46",
)
@@ -62,7 +62,20 @@ def mock_get_setting_values(mock_plenticore_client: ApiClient) -> list:
"id": "Battery:MinHomeComsumption",
}
),
]
],
"scb:network": [
SettingsData(
{
"min": "1",
"default": None,
"access": "readwrite",
"unit": None,
"id": "Hostname",
"type": "string",
"max": "63",
}
)
],
}
# this values are always retrieved by the integration on startup
@@ -112,7 +125,22 @@ async def test_setup_no_entries(
) -> None:
"""Test that no entries are setup if Plenticore does not provide data."""
mock_plenticore_client.get_settings.return_value = []
# remove all settings except hostname which is used during setup
mock_plenticore_client.get_settings.return_value = {
"scb:network": [
SettingsData(
{
"min": "1",
"default": None,
"access": "readwrite",
"unit": None,
"id": "Hostname",
"type": "string",
"max": "63",
}
)
],
}
mock_config_entry.add_to_hass(hass)
+1 -2
View File
@@ -7,7 +7,6 @@ from homeassistant.components.samsungtv.const import DOMAIN, ENTRY_RELOAD_COOLDO
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
from tests.common import MockConfigEntry, async_fire_time_changed
@@ -29,7 +28,7 @@ async def setup_samsungtv_entry(hass: HomeAssistant, data: ConfigType) -> Config
)
entry.add_to_hass(hass)
await async_setup_component(hass, DOMAIN, {})
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
return entry
-9
View File
@@ -290,15 +290,6 @@ def remoteencws_fixture() -> Mock:
yield remoteencws
@pytest.fixture(name="delay")
def delay_fixture() -> Mock:
"""Patch the delay script function."""
with patch(
"homeassistant.components.samsungtv.media_player.Script.async_run"
) as delay:
yield delay
@pytest.fixture
def mock_now() -> datetime:
"""Fixture for dtutil.now."""
@@ -1,10 +0,0 @@
# serializer version: 1
# name: test_setup
IssueRegistryItemSnapshot({
'created': <ANY>,
'dismissed_version': None,
'domain': 'samsungtv',
'is_persistent': False,
'issue_id': 'deprecated_yaml',
})
# ---
+143 -347
View File
@@ -1,5 +1,4 @@
"""Tests for Samsung TV config flow."""
import socket
from unittest.mock import ANY, AsyncMock, Mock, call, patch
import pytest
@@ -28,13 +27,9 @@ from homeassistant.components.samsungtv.const import (
DEFAULT_MANUFACTURER,
DOMAIN,
LEGACY_PORT,
METHOD_ENCRYPTED_WEBSOCKET,
METHOD_LEGACY,
METHOD_WEBSOCKET,
RESULT_AUTH_MISSING,
RESULT_CANNOT_CONNECT,
RESULT_NOT_SUPPORTED,
RESULT_UNKNOWN_HOST,
TIMEOUT_REQUEST,
TIMEOUT_WEBSOCKET,
)
@@ -61,7 +56,6 @@ from homeassistant.data_entry_flow import FlowResultType
from homeassistant.setup import async_setup_component
from .const import (
MOCK_CONFIG_ENCRYPTED_WS,
MOCK_ENTRYDATA_ENCRYPTED_WS,
MOCK_ENTRYDATA_WS,
MOCK_SSDP_DATA_MAIN_TV_AGENT_ST,
@@ -907,136 +901,6 @@ async def test_ssdp_already_configured(hass: HomeAssistant) -> None:
assert entry.unique_id == "123"
@pytest.mark.usefixtures("remote")
async def test_import_legacy(hass: HomeAssistant) -> None:
"""Test importing from yaml with hostname."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_DATA,
)
await hass.async_block_till_done()
assert result["type"] == "create_entry"
assert result["title"] == "fake"
assert result["data"][CONF_HOST] == "fake_host"
assert result["data"][CONF_NAME] == "fake"
assert result["data"][CONF_MANUFACTURER] == "Samsung"
assert result["result"].unique_id is None
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
assert entries[0].data[CONF_METHOD] == METHOD_LEGACY
assert entries[0].data[CONF_PORT] == LEGACY_PORT
@pytest.mark.usefixtures("remote", "remotews", "rest_api_failing")
async def test_import_legacy_without_name(
hass: HomeAssistant, mock_setup_entry: AsyncMock
) -> None:
"""Test importing from yaml without a name."""
with patch(
"homeassistant.components.samsungtv.bridge.SamsungTVEncryptedWSAsyncRemote.start_listening",
side_effect=WebSocketProtocolError("Boom"),
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_DATA_WITHOUT_NAME,
)
await hass.async_block_till_done()
assert result["type"] == "create_entry"
assert result["title"] == "fake_host"
assert result["data"][CONF_HOST] == "fake_host"
assert result["data"][CONF_MANUFACTURER] == "Samsung"
assert result["result"].unique_id is None
mock_setup_entry.assert_called_once()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
# METHOD / PORT failed during import
# They will get checked/set on setup
assert CONF_METHOD not in entries[0].data
assert CONF_PORT not in entries[0].data
@pytest.mark.usefixtures("remotews", "rest_api")
async def test_import_websocket(hass: HomeAssistant) -> None:
"""Test importing from yaml with hostname."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_WSDATA,
)
await hass.async_block_till_done()
assert result["type"] == "create_entry"
assert result["title"] == "fake"
assert result["data"][CONF_METHOD] == METHOD_WEBSOCKET
assert result["data"][CONF_PORT] == 8002
assert result["data"][CONF_HOST] == "fake_host"
assert result["data"][CONF_NAME] == "fake"
assert result["data"][CONF_MANUFACTURER] == "Samsung"
assert result["result"].unique_id is None
@pytest.mark.usefixtures("remoteencws")
async def test_import_websocket_encrypted(hass: HomeAssistant) -> None:
"""Test importing from yaml with hostname."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_CONFIG_ENCRYPTED_WS,
)
await hass.async_block_till_done()
assert result["type"] == "create_entry"
assert result["title"] == "fake"
assert result["data"][CONF_METHOD] == METHOD_ENCRYPTED_WEBSOCKET
assert result["data"][CONF_PORT] == 8000
assert result["data"][CONF_HOST] == "fake_host"
assert result["data"][CONF_NAME] == "fake"
assert result["data"][CONF_MANUFACTURER] == "Samsung"
assert result["result"].unique_id is None
@pytest.mark.usefixtures("remotews", "rest_api")
async def test_import_websocket_without_port(hass: HomeAssistant) -> None:
"""Test importing from yaml with hostname by no port."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_WSDATA,
)
await hass.async_block_till_done()
assert result["type"] == "create_entry"
assert result["title"] == "fake"
assert result["data"][CONF_HOST] == "fake_host"
assert result["data"][CONF_NAME] == "fake"
assert result["data"][CONF_MANUFACTURER] == "Samsung"
assert result["result"].unique_id is None
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
assert entries[0].data[CONF_METHOD] == METHOD_WEBSOCKET
assert entries[0].data[CONF_PORT] == 8002
@pytest.mark.usefixtures("remotews")
async def test_import_unknown_host(hass: HomeAssistant) -> None:
"""Test importing from yaml with hostname that does not resolve."""
with patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
side_effect=socket.gaierror,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_DATA,
)
await hass.async_block_till_done()
assert result["type"] == "abort"
assert result["reason"] == RESULT_UNKNOWN_HOST
@pytest.mark.usefixtures("remotews", "rest_api_non_ssl_only", "remoteencws_failing")
async def test_dhcp_wireless(hass: HomeAssistant) -> None:
"""Test starting a flow from dhcp."""
@@ -1389,18 +1253,14 @@ async def test_update_missing_mac_unique_id_added_from_dhcp(
"""Test missing mac and unique id added."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_OLD_ENTRY, unique_id=None)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=MOCK_DHCP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=MOCK_DHCP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1415,18 +1275,15 @@ async def test_update_missing_mac_unique_id_added_from_zeroconf(
"""Test missing mac and unique id added."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_OLD_ENTRY, unique_id=None)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
data=MOCK_ZEROCONF_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
data=MOCK_ZEROCONF_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
assert entry.data[CONF_MAC] == "aa:bb:zz:ee:rr:oo"
@@ -1444,18 +1301,14 @@ async def test_update_missing_model_added_from_ssdp(
unique_id=None,
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1469,18 +1322,14 @@ async def test_update_missing_mac_unique_id_ssdp_location_added_from_ssdp(
"""Test missing mac, ssdp_location, and unique id added via ssdp."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_OLD_ENTRY, unique_id=None)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1529,18 +1378,14 @@ async def test_update_missing_mac_unique_id_added_ssdp_location_updated_from_ssd
unique_id=None,
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1566,18 +1411,14 @@ async def test_update_missing_mac_unique_id_added_ssdp_location_rendering_st_upd
unique_id=None,
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_RENDERING_CONTROL_ST,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_RENDERING_CONTROL_ST,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1605,18 +1446,14 @@ async def test_update_missing_mac_unique_id_added_ssdp_location_main_tv_agent_st
unique_id=None,
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_MAIN_TV_AGENT_ST,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_MAIN_TV_AGENT_ST,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1644,18 +1481,14 @@ async def test_update_ssdp_location_rendering_st_updated_from_ssdp(
unique_id="be9554b9-c9fb-41f4-8920-22da015376a4",
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_RENDERING_CONTROL_ST,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_RENDERING_CONTROL_ST,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1679,18 +1512,14 @@ async def test_update_main_tv_ssdp_location_rendering_st_updated_from_ssdp(
unique_id="be9554b9-c9fb-41f4-8920-22da015376a4",
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_MAIN_TV_AGENT_ST,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_MAIN_TV_AGENT_ST,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1714,18 +1543,15 @@ async def test_update_missing_mac_added_unique_id_preserved_from_zeroconf(
unique_id="0d1cef00-00dc-1000-9c80-4844f7b172de",
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
data=MOCK_ZEROCONF_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
data=MOCK_ZEROCONF_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
assert entry.data[CONF_MAC] == "aa:bb:zz:ee:rr:oo"
@@ -1743,20 +1569,17 @@ async def test_update_legacy_missing_mac_from_dhcp(
unique_id="0d1cef00-00dc-1000-9c80-4844f7b172de",
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(
ip=EXISTING_IP, macaddress="aa:bb:cc:dd:ee:ff", hostname="fake_hostname"
),
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(
ip=EXISTING_IP, macaddress="aa:bb:cc:dd:ee:ff", hostname="fake_hostname"
),
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
assert entry.data[CONF_MAC] == "aa:bb:cc:dd:ee:ff"
@@ -1780,10 +1603,7 @@ async def test_update_legacy_missing_mac_from_dhcp_no_unique_id(
), patch(
"homeassistant.components.samsungtv.bridge.SamsungTVEncryptedWSAsyncRemote.start_listening",
side_effect=WebSocketProtocolError("Boom"),
), patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
@@ -1792,8 +1612,8 @@ async def test_update_legacy_missing_mac_from_dhcp_no_unique_id(
),
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "not_supported"
assert entry.data[CONF_MAC] == "aa:bb:cc:dd:ee:ff"
@@ -1811,18 +1631,14 @@ async def test_update_ssdp_location_unique_id_added_from_ssdp(
unique_id=None,
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -1843,18 +1659,14 @@ async def test_update_ssdp_location_unique_id_added_from_ssdp_with_rendering_con
unique_id=None,
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_RENDERING_CONTROL_ST,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_RENDERING_CONTROL_ST,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -2060,18 +1872,14 @@ async def test_update_incorrect_udn_matching_upnp_udn_unique_id_added_from_ssdp(
unique_id="0d1cef00-00dc-1000-9c80-4844f7b172de",
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -2090,18 +1898,14 @@ async def test_update_incorrect_udn_matching_mac_unique_id_added_from_ssdp(
unique_id=None,
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -2121,18 +1925,14 @@ async def test_update_incorrect_udn_matching_mac_from_dhcp(
unique_id="0d1cef00-00dc-1000-9c80-4844f7b172de",
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=MOCK_DHCP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=MOCK_DHCP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
@@ -2152,18 +1952,14 @@ async def test_no_update_incorrect_udn_not_matching_mac_from_dhcp(
unique_id="0d1cef00-00dc-1000-9c80-4844f7b172de",
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.samsungtv.async_setup",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=MOCK_DHCP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 0
assert len(mock_setup_entry.mock_calls) == 0
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=MOCK_DHCP_DATA,
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 0
assert result["type"] == "form"
assert result["step_id"] == "confirm"
+10 -76
View File
@@ -2,12 +2,10 @@
from unittest.mock import Mock, patch
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.media_player import DOMAIN, MediaPlayerEntityFeature
from homeassistant.components.samsungtv.const import (
CONF_MANUFACTURER,
CONF_ON_ACTION,
CONF_SESSION_ID,
CONF_SSDP_MAIN_TV_AGENT_LOCATION,
CONF_SSDP_RENDERING_CONTROL_LOCATION,
@@ -32,8 +30,6 @@ from homeassistant.const import (
SERVICE_VOLUME_UP,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from homeassistant.setup import async_setup_component
from . import setup_samsungtv_entry
from .const import (
@@ -48,43 +44,16 @@ from tests.common import MockConfigEntry
ENTITY_ID = f"{DOMAIN}.fake_name"
MOCK_CONFIG = {
SAMSUNGTV_DOMAIN: [
{
CONF_HOST: "fake_host",
CONF_NAME: "fake_name",
CONF_ON_ACTION: [{"delay": "00:00:01"}],
CONF_METHOD: METHOD_WEBSOCKET,
}
]
}
MOCK_CONFIG_WITHOUT_PORT = {
SAMSUNGTV_DOMAIN: [
{
CONF_HOST: "fake_host",
CONF_NAME: "fake",
CONF_ON_ACTION: [{"delay": "00:00:01"}],
}
]
}
REMOTE_CALL = {
"name": "HomeAssistant",
"description": "HomeAssistant",
"id": "ha.component.samsung",
"host": MOCK_CONFIG[SAMSUNGTV_DOMAIN][0][CONF_HOST],
"method": "legacy",
"port": None,
"timeout": 1,
CONF_HOST: "fake_host",
CONF_NAME: "fake_name",
CONF_METHOD: METHOD_WEBSOCKET,
}
@pytest.mark.usefixtures("remotews", "remoteencws_failing", "rest_api")
async def test_setup(
hass: HomeAssistant, issue_registry: ir.IssueRegistry, snapshot: SnapshotAssertion
) -> None:
async def test_setup(hass: HomeAssistant) -> None:
"""Test Samsung TV integration is setup."""
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
await setup_samsungtv_entry(hass, MOCK_CONFIG)
state = hass.states.get(ENTITY_ID)
# test name and turn_on
@@ -100,12 +69,8 @@ async def test_setup(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
# ensure deprecated_yaml issue is raised
issue = issue_registry.async_get_issue(SAMSUNGTV_DOMAIN, "deprecated_yaml")
assert issue == snapshot
async def test_setup_from_yaml_without_port_device_offline(hass: HomeAssistant) -> None:
async def test_setup_without_port_device_offline(hass: HomeAssistant) -> None:
"""Test import from yaml when the device is offline."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote", side_effect=OSError
@@ -119,8 +84,7 @@ async def test_setup_from_yaml_without_port_device_offline(hass: HomeAssistant)
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
return_value=None,
):
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
await setup_samsungtv_entry(hass, MOCK_CONFIG)
config_entries_domain = hass.config_entries.async_entries(SAMSUNGTV_DOMAIN)
assert len(config_entries_domain) == 1
@@ -128,52 +92,22 @@ async def test_setup_from_yaml_without_port_device_offline(hass: HomeAssistant)
@pytest.mark.usefixtures("remotews", "remoteencws_failing", "rest_api")
async def test_setup_from_yaml_without_port_device_online(hass: HomeAssistant) -> None:
async def test_setup_without_port_device_online(hass: HomeAssistant) -> None:
"""Test import from yaml when the device is online."""
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
await setup_samsungtv_entry(hass, MOCK_CONFIG)
config_entries_domain = hass.config_entries.async_entries(SAMSUNGTV_DOMAIN)
assert len(config_entries_domain) == 1
assert config_entries_domain[0].data[CONF_MAC] == "aa:bb:ww:ii:ff:ii"
@pytest.mark.usefixtures("remote")
async def test_setup_duplicate_config(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test duplicate setup of platform."""
duplicate = {
SAMSUNGTV_DOMAIN: [
MOCK_CONFIG[SAMSUNGTV_DOMAIN][0],
MOCK_CONFIG[SAMSUNGTV_DOMAIN][0],
]
}
await async_setup_component(hass, SAMSUNGTV_DOMAIN, duplicate)
await hass.async_block_till_done()
assert hass.states.get(ENTITY_ID) is None
assert len(hass.states.async_all("media_player")) == 0
assert "duplicate host entries found" in caplog.text
@pytest.mark.usefixtures("remotews", "remoteencws_failing", "rest_api")
async def test_setup_duplicate_entries(hass: HomeAssistant) -> None:
"""Test duplicate setup of platform."""
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
assert hass.states.get(ENTITY_ID)
assert len(hass.states.async_all("media_player")) == 1
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
assert len(hass.states.async_all("media_player")) == 1
@pytest.mark.usefixtures("remotews", "remoteencws_failing")
async def test_setup_h_j_model(
hass: HomeAssistant, rest_api: Mock, caplog: pytest.LogCaptureFixture
) -> None:
"""Test Samsung TV integration is setup."""
rest_api.rest_device_info.return_value = SAMPLE_DEVICE_INFO_UE48JU6400
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state
+67 -147
View File
@@ -33,15 +33,14 @@ from homeassistant.components.media_player import (
SERVICE_PLAY_MEDIA,
SERVICE_SELECT_SOURCE,
MediaPlayerDeviceClass,
MediaPlayerEntityFeature,
MediaType,
)
from homeassistant.components.samsungtv.const import (
CONF_ON_ACTION,
CONF_SSDP_RENDERING_CONTROL_LOCATION,
DOMAIN as SAMSUNGTV_DOMAIN,
ENCRYPTED_WEBSOCKET_PORT,
METHOD_ENCRYPTED_WEBSOCKET,
METHOD_LEGACY,
METHOD_WEBSOCKET,
TIMEOUT_WEBSOCKET,
)
@@ -77,8 +76,6 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from . import async_wait_config_entry_reload, setup_samsungtv_entry
@@ -93,25 +90,17 @@ from tests.common import MockConfigEntry, async_fire_time_changed
ENTITY_ID = f"{DOMAIN}.fake"
MOCK_CONFIG = {
SAMSUNGTV_DOMAIN: [
{
CONF_HOST: "fake_host",
CONF_NAME: "fake",
CONF_PORT: 55000,
CONF_ON_ACTION: [{"delay": "00:00:01"}],
}
]
CONF_HOST: "fake_host",
CONF_NAME: "fake",
CONF_PORT: 55000,
CONF_METHOD: METHOD_LEGACY,
}
MOCK_CONFIGWS = {
SAMSUNGTV_DOMAIN: [
{
CONF_HOST: "fake_host",
CONF_NAME: "fake",
CONF_PORT: 8001,
CONF_TOKEN: "123456789",
CONF_ON_ACTION: [{"delay": "00:00:01"}],
}
]
CONF_HOST: "fake_host",
CONF_NAME: "fake",
CONF_PORT: 8001,
CONF_TOKEN: "123456789",
CONF_METHOD: METHOD_WEBSOCKET,
}
MOCK_CALLS_WS = {
CONF_HOST: "fake_host",
@@ -143,43 +132,13 @@ MOCK_ENTRY_WS_WITH_MAC = {
}
ENTITY_ID_NOTURNON = f"{DOMAIN}.fake_noturnon"
MOCK_CONFIG_NOTURNON = {
SAMSUNGTV_DOMAIN: [
{CONF_HOST: "fake_noturnon", CONF_NAME: "fake_noturnon", CONF_PORT: 55000}
]
}
@pytest.fixture(name="delay")
def delay_fixture():
"""Patch the delay script function."""
with patch(
"homeassistant.components.samsungtv.media_player.Script.async_run"
) as delay:
yield delay
async def setup_samsungtv(hass: HomeAssistant, config: ConfigType) -> None:
"""Set up mock Samsung TV."""
await async_setup_component(hass, SAMSUNGTV_DOMAIN, config)
await hass.async_block_till_done()
@pytest.mark.usefixtures("remote")
async def test_setup_with_turnon(hass: HomeAssistant) -> None:
async def test_setup(hass: HomeAssistant) -> None:
"""Test setup of platform."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert hass.states.get(ENTITY_ID)
@pytest.mark.usefixtures("remote")
async def test_setup_without_turnon(hass: HomeAssistant) -> None:
"""Test setup of platform."""
await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON)
assert hass.states.get(ENTITY_ID_NOTURNON)
@pytest.mark.usefixtures("remotews", "rest_api")
async def test_setup_websocket(hass: HomeAssistant) -> None:
"""Test setup of platform."""
@@ -192,7 +151,7 @@ async def test_setup_websocket(hass: HomeAssistant) -> None:
remote.token = "123456789"
remote_class.return_value = remote
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
assert remote_class.call_count == 1
assert remote_class.call_args_list == [call(**MOCK_CALLS_WS)]
@@ -229,7 +188,7 @@ async def test_setup_websocket_2(hass: HomeAssistant, mock_now: datetime) -> Non
remote.__aexit__ = AsyncMock()
remote.token = "987654321"
remote_class.return_value = remote
assert await async_setup_component(hass, SAMSUNGTV_DOMAIN, {})
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert config_entries[0].data[CONF_MAC] == "aa:bb:ww:ii:ff:ii"
@@ -272,7 +231,7 @@ async def test_setup_encrypted_websocket(
@pytest.mark.usefixtures("remote")
async def test_update_on(hass: HomeAssistant, mock_now: datetime) -> None:
"""Testing update tv on."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
next_update = mock_now + timedelta(minutes=5)
with patch("homeassistant.util.dt.utcnow", return_value=next_update):
@@ -286,7 +245,7 @@ async def test_update_on(hass: HomeAssistant, mock_now: datetime) -> None:
@pytest.mark.usefixtures("remote")
async def test_update_off(hass: HomeAssistant, mock_now: datetime) -> None:
"""Testing update tv off."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@@ -298,14 +257,14 @@ async def test_update_off(hass: HomeAssistant, mock_now: datetime) -> None:
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_OFF
assert state.state == STATE_UNAVAILABLE
async def test_update_off_ws_no_power_state(
hass: HomeAssistant, remotews: Mock, rest_api: Mock, mock_now: datetime
) -> None:
"""Testing update tv off."""
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
# device_info should only get called once, as part of the setup
rest_api.rest_device_info.assert_called_once()
rest_api.rest_device_info.reset_mock()
@@ -336,13 +295,13 @@ async def test_update_off_ws_with_power_state(
) as mock_device_info, patch.object(
remotews, "start_listening", side_effect=WebSocketException("Boom")
) as mock_start_listening:
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
mock_device_info.assert_called_once()
mock_start_listening.assert_called_once()
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_OFF
assert state.state == STATE_UNAVAILABLE
# First update uses start_listening once, and initialises device_info
device_info = deepcopy(SAMPLE_DEVICE_INFO_WIFI)
@@ -385,7 +344,7 @@ async def test_update_off_ws_with_power_state(
rest_api.rest_device_info.assert_called_once()
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_OFF
assert state.state == STATE_UNAVAILABLE
remotews.start_listening.assert_not_called()
@@ -417,7 +376,7 @@ async def test_update_off_encryptedws(
@pytest.mark.usefixtures("remote")
async def test_update_access_denied(hass: HomeAssistant, mock_now: datetime) -> None:
"""Testing update tv access denied exception."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@@ -449,7 +408,7 @@ async def test_update_ws_connection_failure(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Testing update tv connection failure exception."""
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
with patch.object(
remotews,
@@ -476,7 +435,7 @@ async def test_update_ws_connection_closed(
hass: HomeAssistant, mock_now: datetime, remotews: Mock
) -> None:
"""Testing update tv connection failure exception."""
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
with patch.object(
remotews, "start_listening", side_effect=ConnectionClosedError(None, None)
@@ -495,7 +454,7 @@ async def test_update_ws_unauthorized_error(
hass: HomeAssistant, mock_now: datetime, remotews: Mock
) -> None:
"""Testing update tv unauthorized failure exception."""
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
with patch.object(
remotews, "start_listening", side_effect=UnauthorizedError
@@ -519,7 +478,7 @@ async def test_update_unhandled_response(
hass: HomeAssistant, mock_now: datetime
) -> None:
"""Testing update tv unhandled response exception."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@@ -539,7 +498,7 @@ async def test_connection_closed_during_update_can_recover(
hass: HomeAssistant, mock_now: datetime
) -> None:
"""Testing update tv connection closed exception can recover."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@@ -551,7 +510,7 @@ async def test_connection_closed_during_update_can_recover(
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_OFF
assert state.state == STATE_UNAVAILABLE
next_update = mock_now + timedelta(minutes=10)
with patch("homeassistant.util.dt.utcnow", return_value=next_update):
@@ -564,7 +523,7 @@ async def test_connection_closed_during_update_can_recover(
async def test_send_key(hass: HomeAssistant, remote: Mock) -> None:
"""Test for send key."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
@@ -579,7 +538,7 @@ async def test_send_key(hass: HomeAssistant, remote: Mock) -> None:
async def test_send_key_broken_pipe(hass: HomeAssistant, remote: Mock) -> None:
"""Testing broken pipe Exception."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.control = Mock(side_effect=BrokenPipeError("Boom"))
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
@@ -592,7 +551,7 @@ async def test_send_key_connection_closed_retry_succeed(
hass: HomeAssistant, remote: Mock
) -> None:
"""Test retry on connection closed."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.control = Mock(
side_effect=[exceptions.ConnectionClosed("Boom"), DEFAULT_MOCK, DEFAULT_MOCK]
)
@@ -613,7 +572,7 @@ async def test_send_key_connection_closed_retry_succeed(
async def test_send_key_unhandled_response(hass: HomeAssistant, remote: Mock) -> None:
"""Testing unhandled response exception."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.control = Mock(side_effect=exceptions.UnhandledResponse("Boom"))
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
@@ -625,7 +584,7 @@ async def test_send_key_unhandled_response(hass: HomeAssistant, remote: Mock) ->
@pytest.mark.usefixtures("rest_api")
async def test_send_key_websocketexception(hass: HomeAssistant, remotews: Mock) -> None:
"""Testing unhandled response exception."""
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
remotews.send_commands = Mock(side_effect=WebSocketException("Boom"))
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
@@ -651,7 +610,7 @@ async def test_send_key_websocketexception_encrypted(
@pytest.mark.usefixtures("rest_api")
async def test_send_key_os_error_ws(hass: HomeAssistant, remotews: Mock) -> None:
"""Testing unhandled response exception."""
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
remotews.send_commands = Mock(side_effect=OSError("Boom"))
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
@@ -676,7 +635,7 @@ async def test_send_key_os_error_ws_encrypted(
async def test_send_key_os_error(hass: HomeAssistant, remote: Mock) -> None:
"""Testing broken pipe Exception."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.control = Mock(side_effect=OSError("Boom"))
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
@@ -688,42 +647,24 @@ async def test_send_key_os_error(hass: HomeAssistant, remote: Mock) -> None:
@pytest.mark.usefixtures("remote")
async def test_name(hass: HomeAssistant) -> None:
"""Test for name property."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
state = hass.states.get(ENTITY_ID)
assert state.attributes[ATTR_FRIENDLY_NAME] == "fake"
@pytest.mark.usefixtures("remote")
async def test_state_with_turnon(hass: HomeAssistant, delay: Mock) -> None:
async def test_state(hass: HomeAssistant) -> None:
"""Test for state property."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, True
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_ON
assert delay.call_count == 1
assert await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_OFF
@pytest.mark.usefixtures("remote")
async def test_state_without_turnon(hass: HomeAssistant) -> None:
"""Test for state property."""
await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON)
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True
)
state = hass.states.get(ENTITY_ID_NOTURNON)
assert state.state == STATE_ON
assert await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True
)
state = hass.states.get(ENTITY_ID_NOTURNON)
# Should be STATE_UNAVAILABLE after the timer expires
assert state.state == STATE_OFF
@@ -735,34 +676,23 @@ async def test_state_without_turnon(hass: HomeAssistant) -> None:
async_fire_time_changed(hass, next_update)
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID_NOTURNON)
state = hass.states.get(ENTITY_ID)
# Should be STATE_UNAVAILABLE since there is no way to turn it back on
assert state.state == STATE_UNAVAILABLE
@pytest.mark.usefixtures("remote")
async def test_supported_features_with_turnon(hass: HomeAssistant) -> None:
async def test_supported_features(hass: HomeAssistant) -> None:
"""Test for supported_features property."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
state = hass.states.get(ENTITY_ID)
assert (
state.attributes[ATTR_SUPPORTED_FEATURES]
== SUPPORT_SAMSUNGTV | MediaPlayerEntityFeature.TURN_ON
)
@pytest.mark.usefixtures("remote")
async def test_supported_features_without_turnon(hass: HomeAssistant) -> None:
"""Test for supported_features property."""
await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON)
state = hass.states.get(ENTITY_ID_NOTURNON)
assert state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_SAMSUNGTV
@pytest.mark.usefixtures("remote")
async def test_device_class(hass: HomeAssistant) -> None:
"""Test for device_class property."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
state = hass.states.get(ENTITY_ID)
assert state.attributes[ATTR_DEVICE_CLASS] is MediaPlayerDeviceClass.TV.value
@@ -777,7 +707,7 @@ async def test_turn_off_websocket(
"homeassistant.components.samsungtv.bridge.Remote",
side_effect=[OSError("Boom"), DEFAULT_MOCK],
):
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
remotews.send_commands.reset_mock()
@@ -816,7 +746,7 @@ async def test_turn_off_websocket_frame(
"homeassistant.components.samsungtv.bridge.Remote",
side_effect=[OSError("Boom"), DEFAULT_MOCK],
):
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
remotews.send_commands.reset_mock()
@@ -903,9 +833,9 @@ async def test_turn_off_encrypted_websocket_key_type(
async def test_turn_off_legacy(hass: HomeAssistant, remote: Mock) -> None:
"""Test for turn_off."""
await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
# key called
assert remote.control.call_count == 1
@@ -917,7 +847,7 @@ async def test_turn_off_os_error(
) -> None:
"""Test for turn_off with OSError."""
caplog.set_level(logging.DEBUG)
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.close = Mock(side_effect=OSError("BOOM"))
assert await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True
@@ -931,7 +861,7 @@ async def test_turn_off_ws_os_error(
) -> None:
"""Test for turn_off with OSError."""
caplog.set_level(logging.DEBUG)
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
remotews.close = Mock(side_effect=OSError("BOOM"))
assert await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True
@@ -955,7 +885,7 @@ async def test_turn_off_encryptedws_os_error(
async def test_volume_up(hass: HomeAssistant, remote: Mock) -> None:
"""Test for volume_up."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
@@ -968,7 +898,7 @@ async def test_volume_up(hass: HomeAssistant, remote: Mock) -> None:
async def test_volume_down(hass: HomeAssistant, remote: Mock) -> None:
"""Test for volume_down."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_DOWN, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
@@ -981,7 +911,7 @@ async def test_volume_down(hass: HomeAssistant, remote: Mock) -> None:
async def test_mute_volume(hass: HomeAssistant, remote: Mock) -> None:
"""Test for mute_volume."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN,
SERVICE_VOLUME_MUTE,
@@ -997,7 +927,7 @@ async def test_mute_volume(hass: HomeAssistant, remote: Mock) -> None:
async def test_media_play(hass: HomeAssistant, remote: Mock) -> None:
"""Test for media_play."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_MEDIA_PLAY, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
@@ -1019,7 +949,7 @@ async def test_media_play(hass: HomeAssistant, remote: Mock) -> None:
async def test_media_pause(hass: HomeAssistant, remote: Mock) -> None:
"""Test for media_pause."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_MEDIA_PAUSE, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
@@ -1041,7 +971,7 @@ async def test_media_pause(hass: HomeAssistant, remote: Mock) -> None:
async def test_media_next_track(hass: HomeAssistant, remote: Mock) -> None:
"""Test for media_next_track."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_MEDIA_NEXT_TRACK, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
@@ -1054,7 +984,7 @@ async def test_media_next_track(hass: HomeAssistant, remote: Mock) -> None:
async def test_media_previous_track(hass: HomeAssistant, remote: Mock) -> None:
"""Test for media_previous_track."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
@@ -1065,16 +995,6 @@ async def test_media_previous_track(hass: HomeAssistant, remote: Mock) -> None:
assert remote.close.call_args_list == [call()]
@pytest.mark.usefixtures("remote")
async def test_turn_on_with_turnon(hass: HomeAssistant, delay: Mock) -> None:
"""Test turn on."""
await setup_samsungtv(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
assert delay.call_count == 1
@pytest.mark.usefixtures("remotews", "rest_api")
async def test_turn_on_wol(hass: HomeAssistant) -> None:
"""Test turn on."""
@@ -1084,7 +1004,7 @@ async def test_turn_on_wol(hass: HomeAssistant) -> None:
unique_id="any",
)
entry.add_to_hass(hass)
assert await async_setup_component(hass, SAMSUNGTV_DOMAIN, {})
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
with patch(
"homeassistant.components.samsungtv.media_player.send_magic_packet"
@@ -1098,10 +1018,10 @@ async def test_turn_on_wol(hass: HomeAssistant) -> None:
async def test_turn_on_without_turnon(hass: HomeAssistant, remote: Mock) -> None:
"""Test turn on."""
await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
# nothing called as not supported feature
assert remote.control.call_count == 0
@@ -1109,7 +1029,7 @@ async def test_turn_on_without_turnon(hass: HomeAssistant, remote: Mock) -> None
async def test_play_media(hass: HomeAssistant, remote: Mock) -> None:
"""Test for play_media."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
with patch("homeassistant.components.samsungtv.bridge.asyncio.sleep") as sleep:
assert await hass.services.async_call(
DOMAIN,
@@ -1138,7 +1058,7 @@ async def test_play_media_invalid_type(hass: HomeAssistant) -> None:
"""Test for play_media with invalid media type."""
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote:
url = "https://example.com"
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.reset_mock()
assert await hass.services.async_call(
DOMAIN,
@@ -1160,7 +1080,7 @@ async def test_play_media_channel_as_string(hass: HomeAssistant) -> None:
"""Test for play_media with invalid channel as string."""
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote:
url = "https://example.com"
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.reset_mock()
assert await hass.services.async_call(
DOMAIN,
@@ -1181,7 +1101,7 @@ async def test_play_media_channel_as_string(hass: HomeAssistant) -> None:
async def test_play_media_channel_as_non_positive(hass: HomeAssistant) -> None:
"""Test for play_media with invalid channel as non positive integer."""
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote:
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.reset_mock()
assert await hass.services.async_call(
DOMAIN,
@@ -1201,7 +1121,7 @@ async def test_play_media_channel_as_non_positive(hass: HomeAssistant) -> None:
async def test_select_source(hass: HomeAssistant, remote: Mock) -> None:
"""Test for select_source."""
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
assert await hass.services.async_call(
DOMAIN,
SERVICE_SELECT_SOURCE,
@@ -1218,7 +1138,7 @@ async def test_select_source(hass: HomeAssistant, remote: Mock) -> None:
async def test_select_source_invalid_source(hass: HomeAssistant) -> None:
"""Test for select_source with invalid source."""
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote:
await setup_samsungtv(hass, MOCK_CONFIG)
await setup_samsungtv_entry(hass, MOCK_CONFIG)
remote.reset_mock()
assert await hass.services.async_call(
DOMAIN,
@@ -1235,7 +1155,7 @@ async def test_select_source_invalid_source(hass: HomeAssistant) -> None:
@pytest.mark.usefixtures("rest_api")
async def test_play_media_app(hass: HomeAssistant, remotews: Mock) -> None:
"""Test for play_media."""
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
remotews.send_commands.reset_mock()
assert await hass.services.async_call(
@@ -1259,7 +1179,7 @@ async def test_play_media_app(hass: HomeAssistant, remotews: Mock) -> None:
async def test_select_source_app(hass: HomeAssistant, remotews: Mock) -> None:
"""Test for select_source."""
remotews.app_list_data = SAMPLE_EVENT_ED_INSTALLED_APP
await setup_samsungtv(hass, MOCK_CONFIGWS)
await setup_samsungtv_entry(hass, MOCK_CONFIGWS)
remotews.send_commands.reset_mock()
assert await hass.services.async_call(
+3 -3
View File
@@ -858,7 +858,7 @@ async def test_options_flow_disabled_gen_1(
await ws_client.send_json(
{
"id": 5,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"domain": "shelly",
}
)
@@ -879,7 +879,7 @@ async def test_options_flow_enabled_gen_2(
await ws_client.send_json(
{
"id": 5,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"domain": "shelly",
}
)
@@ -900,7 +900,7 @@ async def test_options_flow_disabled_sleepy_gen_2(
await ws_client.send_json(
{
"id": 5,
"type": "config_entries/get",
"type": "config_entries/get_matching",
"domain": "shelly",
}
)