Merge pull request #66686 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2022-02-16 14:55:42 -08:00 committed by GitHub
commit e3a2b51f03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 218 additions and 226 deletions

View file

@ -946,8 +946,6 @@ omit =
homeassistant/components/russound_rnet/media_player.py
homeassistant/components/sabnzbd/*
homeassistant/components/saj/sensor.py
homeassistant/components/samsungtv/bridge.py
homeassistant/components/samsungtv/diagnostics.py
homeassistant/components/satel_integra/*
homeassistant/components/schluter/*
homeassistant/components/scrape/sensor.py

View file

@ -11,7 +11,6 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DATA_BAPI, DATA_COOR, DOMAIN, PLATFORMS, REGULAR_INTERVAL
@ -21,11 +20,9 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Brunt using config flow."""
session = async_get_clientsession(hass)
bapi = BruntClientAsync(
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
session=session,
)
try:
await bapi.async_login()

View file

@ -187,7 +187,11 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
self._alexa_sync_unsub = None
return
if ALEXA_DOMAIN not in self.hass.config.components and self.enabled:
if (
ALEXA_DOMAIN not in self.hass.config.components
and self.enabled
and self.hass.is_running
):
await async_setup_component(self.hass, ALEXA_DOMAIN, {})
if self.should_report_state != self.is_reporting_states:

View file

@ -181,7 +181,11 @@ class CloudGoogleConfig(AbstractConfig):
self.async_disable_local_sdk()
return
if self.enabled and GOOGLE_DOMAIN not in self.hass.config.components:
if (
self.enabled
and GOOGLE_DOMAIN not in self.hass.config.components
and self.hass.is_running
):
await async_setup_component(self.hass, GOOGLE_DOMAIN, {})
if self.should_report_state != self.is_reporting_state:

View file

@ -2,7 +2,7 @@
"domain": "goalfeed",
"name": "Goalfeed",
"documentation": "https://www.home-assistant.io/integrations/goalfeed",
"requirements": ["pysher==1.0.1"],
"requirements": ["pysher==1.0.7"],
"codeowners": [],
"iot_class": "cloud_push"
}

View file

@ -3,7 +3,7 @@
"name": "Philips Hue",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/hue",
"requirements": ["aiohue==4.1.2"],
"requirements": ["aiohue==4.2.0"],
"ssdp": [
{
"manufacturer": "Royal Philips Electronics",

View file

@ -72,7 +72,7 @@ async def async_setup_entry(
vol.Coerce(int), vol.Range(min=0, max=255)
),
},
"async_activate",
"_async_activate",
)

View file

@ -20,6 +20,7 @@ from homeassistant.const import (
CONF_PAYLOAD_OFF,
CONF_PAYLOAD_ON,
CONF_VALUE_TEMPLATE,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
@ -105,7 +106,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity):
def __init__(self, hass, config, config_entry, discovery_data):
"""Initialize the MQTT binary sensor."""
self._state = None
self._state: bool | None = None
self._expiration_trigger = None
self._delay_listener = None
expire_after = config.get(CONF_EXPIRE_AFTER)
@ -124,6 +125,9 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity):
and expire_after > 0
and (last_state := await self.async_get_last_state()) is not None
and last_state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE]
# We might have set up a trigger already after subscribing from
# super().async_added_to_hass(), then we should not restore state
and not self._expiration_trigger
):
expiration_at = last_state.last_changed + timedelta(seconds=expire_after)
if expiration_at < (time_now := dt_util.utcnow()):
@ -131,12 +135,8 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity):
_LOGGER.debug("Skip state recovery after reload for %s", self.entity_id)
return
self._expired = False
self._state = last_state.state
self._state = last_state.state == STATE_ON
if self._expiration_trigger:
# We might have set up a trigger already after subscribing from
# super().async_added_to_hass()
self._expiration_trigger()
self._expiration_trigger = async_track_point_in_utc_time(
self.hass, self._value_is_expired, expiration_at
)

View file

@ -171,6 +171,9 @@ class MqttSensor(MqttEntity, SensorEntity, RestoreEntity):
and expire_after > 0
and (last_state := await self.async_get_last_state()) is not None
and last_state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE]
# We might have set up a trigger already after subscribing from
# super().async_added_to_hass(), then we should not restore state
and not self._expiration_trigger
):
expiration_at = last_state.last_changed + timedelta(seconds=expire_after)
if expiration_at < (time_now := dt_util.utcnow()):
@ -180,10 +183,6 @@ class MqttSensor(MqttEntity, SensorEntity, RestoreEntity):
self._expired = False
self._state = last_state.state
if self._expiration_trigger:
# We might have set up a trigger already after subscribing from
# super().async_added_to_hass()
self._expiration_trigger()
self._expiration_trigger = async_track_point_in_utc_time(
self.hass, self._value_is_expired, expiration_at
)

View file

@ -358,6 +358,13 @@ class SamsungTVWSBridge(SamsungTVBridge):
self._notify_callback()
except (WebSocketException, OSError):
self._remote = None
else:
if self.token != self._remote.token:
LOGGER.debug(
"SamsungTVWSBridge has provided a new token %s",
self._remote.token,
)
self.token = self._remote.token
return self._remote
def stop(self) -> None:

View file

@ -41,15 +41,15 @@ class IntegerTypeData:
@property
def step_scaled(self) -> float:
"""Return the step scaled."""
return self.scale_value(self.step)
return self.step / (10 ** self.scale)
def scale_value(self, value: float | int) -> float:
"""Scale a value."""
return value * 1.0 / (10 ** self.scale)
return value * self.step / (10 ** self.scale)
def scale_value_back(self, value: float | int) -> int:
"""Return raw value for scaled."""
return int(value * (10 ** self.scale))
return int((value * (10 ** self.scale)) / self.step)
def remap_value_to(
self,
@ -82,7 +82,7 @@ class IntegerTypeData:
min=int(parsed["min"]),
max=int(parsed["max"]),
scale=float(parsed["scale"]),
step=float(parsed["step"]),
step=max(float(parsed["step"]), 1),
unit=parsed.get("unit"),
type=parsed.get("type"),
)

View file

@ -155,8 +155,12 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity):
self._attr_temperature_unit = TEMP_CELSIUS
# Figure out current temperature, use preferred unit or what is available
celsius_type = self.find_dpcode(DPCode.TEMP_CURRENT, dptype=DPType.INTEGER)
farhenheit_type = self.find_dpcode(DPCode.TEMP_CURRENT_F, dptype=DPType.INTEGER)
celsius_type = self.find_dpcode(
(DPCode.TEMP_CURRENT, DPCode.UPPER_TEMP), dptype=DPType.INTEGER
)
farhenheit_type = self.find_dpcode(
(DPCode.TEMP_CURRENT_F, DPCode.UPPER_TEMP_F), dptype=DPType.INTEGER
)
if farhenheit_type and (
prefered_temperature_unit == TEMP_FAHRENHEIT
or (prefered_temperature_unit == TEMP_CELSIUS and not celsius_type)

View file

@ -345,6 +345,11 @@ class DPCode(StrEnum):
TOTAL_CLEAN_COUNT = "total_clean_count"
TOTAL_CLEAN_TIME = "total_clean_time"
TOTAL_FORWARD_ENERGY = "total_forward_energy"
TOTAL_TIME = "total_time"
TOTAL_PM = "total_pm"
TVOC = "tvoc"
UPPER_TEMP = "upper_temp"
UPPER_TEMP_F = "upper_temp_f"
UV = "uv" # UV sterilization
VA_BATTERY = "va_battery"
VA_HUMIDITY = "va_humidity"

View file

@ -7,7 +7,7 @@ from .backports.enum import StrEnum
MAJOR_VERSION: Final = 2022
MINOR_VERSION: Final = 2
PATCH_VERSION: Final = "7"
PATCH_VERSION: Final = "8"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)

View file

@ -191,7 +191,7 @@ aiohomekit==0.6.11
aiohttp_cors==0.7.0
# homeassistant.components.hue
aiohue==4.1.2
aiohue==4.2.0
# homeassistant.components.homewizard
aiohwenergy==0.8.0
@ -1831,7 +1831,7 @@ pyserial==3.5
pysesame2==1.0.1
# homeassistant.components.goalfeed
pysher==1.0.1
pysher==1.0.7
# homeassistant.components.sia
pysiaalarm==3.0.2

View file

@ -141,7 +141,7 @@ aiohomekit==0.6.11
aiohttp_cors==0.7.0
# homeassistant.components.hue
aiohue==4.1.2
aiohue==4.2.0
# homeassistant.components.homewizard
aiohwenergy==0.8.0

View file

@ -1,6 +1,6 @@
[metadata]
name = homeassistant
version = 2022.2.7
version = 2022.2.8
author = The Home Assistant Authors
author_email = hello@home-assistant.io
license = Apache-2.0

View file

@ -1,14 +1 @@
"""Tests for the samsungtv component."""
from homeassistant.components.samsungtv.const import DOMAIN as SAMSUNGTV_DOMAIN
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_samsungtv(hass: HomeAssistant, config: dict):
"""Set up mock Samsung TV."""
entry = MockConfigEntry(domain=SAMSUNGTV_DOMAIN, data=config)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()

View file

@ -2,26 +2,29 @@
from unittest.mock import Mock, patch
import pytest
from samsungctl import Remote
from samsungtvws import SamsungTVWS
import homeassistant.util.dt as dt_util
RESULT_ALREADY_CONFIGURED = "already_configured"
RESULT_ALREADY_IN_PROGRESS = "already_in_progress"
@pytest.fixture(autouse=True)
def fake_host_fixture() -> None:
"""Patch gethostbyname."""
with patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
yield
@pytest.fixture(name="remote")
def remote_fixture():
"""Patch the samsungctl Remote."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote"
) as remote_class, patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
remote = Mock()
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote_class:
remote = Mock(Remote)
remote.__enter__ = Mock()
remote.__exit__ = Mock()
remote.port.return_value = 55000
remote_class.return_value = remote
yield remote
@ -31,14 +34,10 @@ def remotews_fixture():
"""Patch the samsungtvws SamsungTVWS."""
with patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
) as remotews_class, patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
remotews = Mock()
remotews.__enter__ = Mock()
) as remotews_class:
remotews = Mock(SamsungTVWS)
remotews.__enter__ = Mock(return_value=remotews)
remotews.__exit__ = Mock()
remotews.port.return_value = 8002
remotews.rest_device_info.return_value = {
"id": "uuid:be9554b9-c9fb-41f4-8920-22da015376a4",
"device": {
@ -49,8 +48,8 @@ def remotews_fixture():
"networkType": "wireless",
},
}
remotews.token = "FAKE_TOKEN"
remotews_class.return_value = remotews
remotews_class().__enter__().token = "FAKE_TOKEN"
yield remotews
@ -59,16 +58,13 @@ def remotews_no_device_info_fixture():
"""Patch the samsungtvws SamsungTVWS."""
with patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
) as remotews_class, patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
remotews = Mock()
remotews.__enter__ = Mock()
) as remotews_class:
remotews = Mock(SamsungTVWS)
remotews.__enter__ = Mock(return_value=remotews)
remotews.__exit__ = Mock()
remotews.rest_device_info.return_value = None
remotews.token = "FAKE_TOKEN"
remotews_class.return_value = remotews
remotews_class().__enter__().token = "FAKE_TOKEN"
yield remotews
@ -77,12 +73,9 @@ def remotews_soundbar_fixture():
"""Patch the samsungtvws SamsungTVWS."""
with patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
) as remotews_class, patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
remotews = Mock()
remotews.__enter__ = Mock()
) as remotews_class:
remotews = Mock(SamsungTVWS)
remotews.__enter__ = Mock(return_value=remotews)
remotews.__exit__ = Mock()
remotews.rest_device_info.return_value = {
"id": "uuid:be9554b9-c9fb-41f4-8920-22da015376a4",
@ -94,8 +87,8 @@ def remotews_soundbar_fixture():
"type": "Samsung SoundBar",
},
}
remotews.token = "FAKE_TOKEN"
remotews_class.return_value = remotews
remotews_class().__enter__().token = "FAKE_TOKEN"
yield remotews

View file

@ -1,8 +1,9 @@
"""Tests for Samsung TV config flow."""
import socket
from unittest.mock import Mock, PropertyMock, call, patch
from unittest.mock import Mock, call, patch
from samsungctl.exceptions import AccessDenied, UnhandledResponse
from samsungtvws import SamsungTVWS
from samsungtvws.exceptions import ConnectionFailure, HttpApiError
from websocket import WebSocketException, WebSocketProtocolException
@ -43,10 +44,9 @@ from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
from tests.components.samsungtv.conftest import (
RESULT_ALREADY_CONFIGURED,
RESULT_ALREADY_IN_PROGRESS,
)
RESULT_ALREADY_CONFIGURED = "already_configured"
RESULT_ALREADY_IN_PROGRESS = "already_in_progress"
MOCK_IMPORT_DATA = {
CONF_HOST: "fake_host",
@ -232,9 +232,7 @@ async def test_user_websocket(hass: HomeAssistant, remotews: Mock):
assert result["result"].unique_id == "be9554b9-c9fb-41f4-8920-22da015376a4"
async def test_user_legacy_missing_auth(
hass: HomeAssistant, remote: Mock, remotews: Mock
):
async def test_user_legacy_missing_auth(hass: HomeAssistant, remotews: Mock):
"""Test starting a flow by user with authentication."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -248,7 +246,7 @@ async def test_user_legacy_missing_auth(
assert result["reason"] == RESULT_AUTH_MISSING
async def test_user_legacy_not_supported(hass: HomeAssistant, remote: Mock):
async def test_user_legacy_not_supported(hass: HomeAssistant):
"""Test starting a flow by user for not supported device."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -262,7 +260,7 @@ async def test_user_legacy_not_supported(hass: HomeAssistant, remote: Mock):
assert result["reason"] == RESULT_NOT_SUPPORTED
async def test_user_websocket_not_supported(hass: HomeAssistant, remotews: Mock):
async def test_user_websocket_not_supported(hass: HomeAssistant):
"""Test starting a flow by user for not supported device."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -279,7 +277,7 @@ async def test_user_websocket_not_supported(hass: HomeAssistant, remotews: Mock)
assert result["reason"] == RESULT_NOT_SUPPORTED
async def test_user_not_successful(hass: HomeAssistant, remotews: Mock):
async def test_user_not_successful(hass: HomeAssistant):
"""Test starting a flow by user but no connection found."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -295,7 +293,7 @@ async def test_user_not_successful(hass: HomeAssistant, remotews: Mock):
assert result["reason"] == RESULT_CANNOT_CONNECT
async def test_user_not_successful_2(hass: HomeAssistant, remotews: Mock):
async def test_user_not_successful_2(hass: HomeAssistant):
"""Test starting a flow by user but no connection found."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -374,9 +372,7 @@ async def test_ssdp_noprefix(hass: HomeAssistant, remote: Mock, no_mac_address:
assert result["result"].unique_id == "0d1cef00-00dc-1000-9c80-4844f7b172df"
async def test_ssdp_legacy_missing_auth(
hass: HomeAssistant, remote: Mock, remotews: Mock
):
async def test_ssdp_legacy_missing_auth(hass: HomeAssistant, remotews: Mock):
"""Test starting a flow from discovery with authentication."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -452,7 +448,7 @@ async def test_ssdp_websocket_success_populates_mac_address(
assert result["result"].unique_id == "0d1cef00-00dc-1000-9c80-4844f7b172de"
async def test_ssdp_websocket_not_supported(hass: HomeAssistant, remote: Mock):
async def test_ssdp_websocket_not_supported(hass: HomeAssistant):
"""Test starting a flow from discovery for not supported device."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -482,9 +478,7 @@ async def test_ssdp_model_not_supported(hass: HomeAssistant, remote: Mock):
assert result["reason"] == RESULT_NOT_SUPPORTED
async def test_ssdp_not_successful(
hass: HomeAssistant, remote: Mock, no_mac_address: Mock
):
async def test_ssdp_not_successful(hass: HomeAssistant, no_mac_address: Mock):
"""Test starting a flow from discovery but no device found."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -512,9 +506,7 @@ async def test_ssdp_not_successful(
assert result["reason"] == RESULT_CANNOT_CONNECT
async def test_ssdp_not_successful_2(
hass: HomeAssistant, remote: Mock, no_mac_address: Mock
):
async def test_ssdp_not_successful_2(hass: HomeAssistant, no_mac_address: Mock):
"""Test starting a flow from discovery but no device found."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -604,15 +596,11 @@ async def test_import_legacy(hass: HomeAssistant, remote: Mock, no_mac_address:
"""Test importing from yaml with hostname."""
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
with patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_DATA,
)
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"
@ -634,15 +622,11 @@ async def test_import_legacy_without_name(
no_mac_address: Mock,
):
"""Test importing from yaml without a name."""
with patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_DATA_WITHOUT_NAME,
)
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"
@ -658,15 +642,11 @@ async def test_import_legacy_without_name(
async def test_import_websocket(hass: HomeAssistant, remotews: Mock):
"""Test importing from yaml with hostname."""
with patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_WSDATA,
)
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"
@ -680,15 +660,11 @@ async def test_import_websocket(hass: HomeAssistant, remotews: Mock):
async def test_import_websocket_without_port(hass: HomeAssistant, remotews: Mock):
"""Test importing from yaml with hostname by no port."""
with patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=MOCK_IMPORT_WSDATA,
)
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"
@ -817,21 +793,14 @@ async def test_zeroconf_and_dhcp_same_time(hass: HomeAssistant, remotews: Mock):
assert result2["reason"] == "already_in_progress"
async def test_autodetect_websocket(hass: HomeAssistant, remote: Mock, remotews: Mock):
async def test_autodetect_websocket(hass: HomeAssistant):
"""Test for send key with autodetection of protocol."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
side_effect=OSError("Boom"),
), patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
), patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
) as remotews:
enter = Mock()
type(enter).token = PropertyMock(return_value="123456789")
remote = Mock()
remote.__enter__ = Mock(return_value=enter)
), patch("homeassistant.components.samsungtv.bridge.SamsungTVWS") as remotews:
remote = Mock(SamsungTVWS)
remote.__enter__ = Mock(return_value=remote)
remote.__exit__ = Mock(return_value=False)
remote.rest_device_info.return_value = {
"id": "uuid:be9554b9-c9fb-41f4-8920-22da015376a4",
@ -845,6 +814,7 @@ async def test_autodetect_websocket(hass: HomeAssistant, remote: Mock, remotews:
"type": "Samsung SmartTV",
},
}
remote.token = "123456789"
remotews.return_value = remote
result = await hass.config_entries.flow.async_init(
@ -865,23 +835,18 @@ async def test_autodetect_websocket(hass: HomeAssistant, remote: Mock, remotews:
assert entries[0].data[CONF_MAC] == "aa:bb:cc:dd:ee:ff"
async def test_websocket_no_mac(hass: HomeAssistant, remote: Mock, remotews: Mock):
async def test_websocket_no_mac(hass: HomeAssistant):
"""Test for send key with autodetection of protocol."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
side_effect=OSError("Boom"),
), patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
), patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
) as remotews, patch(
"getmac.get_mac_address", return_value="gg:hh:ii:ll:mm:nn"
):
enter = Mock()
type(enter).token = PropertyMock(return_value="123456789")
remote = Mock()
remote.__enter__ = Mock(return_value=enter)
remote = Mock(SamsungTVWS)
remote.__enter__ = Mock(return_value=remote)
remote.__exit__ = Mock(return_value=False)
remote.rest_device_info.return_value = {
"id": "uuid:be9554b9-c9fb-41f4-8920-22da015376a4",
@ -893,6 +858,7 @@ async def test_websocket_no_mac(hass: HomeAssistant, remote: Mock, remotews: Moc
"type": "Samsung SmartTV",
},
}
remote.token = "123456789"
remotews.return_value = remote
result = await hass.config_entries.flow.async_init(
@ -914,15 +880,12 @@ async def test_websocket_no_mac(hass: HomeAssistant, remote: Mock, remotews: Moc
assert entries[0].data[CONF_MAC] == "gg:hh:ii:ll:mm:nn"
async def test_autodetect_auth_missing(hass: HomeAssistant, remote: Mock):
async def test_autodetect_auth_missing(hass: HomeAssistant):
"""Test for send key with autodetection of protocol."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
side_effect=[AccessDenied("Boom")],
) as remote, patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
) as remote:
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_USER_DATA
)
@ -932,15 +895,12 @@ async def test_autodetect_auth_missing(hass: HomeAssistant, remote: Mock):
assert remote.call_args_list == [call(AUTODETECT_LEGACY)]
async def test_autodetect_not_supported(hass: HomeAssistant, remote: Mock):
async def test_autodetect_not_supported(hass: HomeAssistant):
"""Test for send key with autodetection of protocol."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
side_effect=[UnhandledResponse("Boom")],
) as remote, patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
) as remote:
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_USER_DATA
)
@ -962,7 +922,7 @@ async def test_autodetect_legacy(hass: HomeAssistant, remote: Mock):
assert result["data"][CONF_PORT] == LEGACY_PORT
async def test_autodetect_none(hass: HomeAssistant, remote: Mock, remotews: Mock):
async def test_autodetect_none(hass: HomeAssistant):
"""Test for send key with autodetection of protocol."""
with patch(
"homeassistant.components.samsungtv.bridge.Remote",
@ -970,10 +930,7 @@ async def test_autodetect_none(hass: HomeAssistant, remote: Mock, remotews: Mock
) as remote, patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS",
side_effect=OSError("Boom"),
) as remotews, patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
) as remotews:
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_USER_DATA
)
@ -990,7 +947,7 @@ async def test_autodetect_none(hass: HomeAssistant, remote: Mock, remotews: Mock
]
async def test_update_old_entry(hass: HomeAssistant, remote: Mock, remotews: Mock):
async def test_update_old_entry(hass: HomeAssistant, remotews: Mock):
"""Test update of old entry."""
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote:
remote().rest_device_info.return_value = {
@ -1266,9 +1223,6 @@ async def test_form_reauth_websocket_cannot_connect(hass, remotews: Mock):
with patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS",
side_effect=ConnectionFailure,
), patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -1289,7 +1243,7 @@ async def test_form_reauth_websocket_cannot_connect(hass, remotews: Mock):
assert result3["reason"] == "reauth_successful"
async def test_form_reauth_websocket_not_supported(hass, remotews: Mock):
async def test_form_reauth_websocket_not_supported(hass):
"""Test reauthenticate websocket when the device is not supported."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_WS_ENTRY)
entry.add_to_hass(hass)
@ -1304,9 +1258,6 @@ async def test_form_reauth_websocket_not_supported(hass, remotews: Mock):
with patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWS",
side_effect=WebSocketException,
), patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],

View file

@ -0,0 +1,59 @@
"""Test samsungtv diagnostics."""
from aiohttp import ClientSession
import pytest
from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.samsungtv import DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .test_media_player import MOCK_ENTRY_WS_WITH_MAC
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
@pytest.fixture(name="config_entry")
def get_config_entry(hass: HomeAssistant) -> ConfigEntry:
"""Create and register mock config entry."""
config_entry = MockConfigEntry(
domain=DOMAIN,
data=MOCK_ENTRY_WS_WITH_MAC,
entry_id="123456",
unique_id="any",
)
config_entry.add_to_hass(hass)
return config_entry
@pytest.mark.usefixtures("remotews")
async def test_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry, hass_client: ClientSession
) -> None:
"""Test config entry diagnostics."""
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"entry": {
"data": {
"host": "fake_host",
"ip_address": "test",
"mac": "aa:bb:cc:dd:ee:ff",
"method": "websocket",
"name": "fake",
"port": 8002,
"token": REDACTED,
},
"disabled_by": None,
"domain": "samsungtv",
"entry_id": "123456",
"options": {},
"pref_disable_new_entities": False,
"pref_disable_polling": False,
"source": "user",
"title": "Mock Title",
"unique_id": "any",
"version": 2,
}
}

View file

@ -55,27 +55,21 @@ REMOTE_CALL = {
async def test_setup(hass: HomeAssistant, remotews: Mock, no_mac_address: Mock):
"""Test Samsung TV integration is setup."""
with patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID)
# test name and turn_on
assert state
assert state.name == "fake_name"
assert (
state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_SAMSUNGTV | SUPPORT_TURN_ON
)
# test name and turn_on
assert state
assert state.name == "fake_name"
assert (
state.attributes[ATTR_SUPPORTED_FEATURES]
== SUPPORT_SAMSUNGTV | SUPPORT_TURN_ON
)
# test host and port
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
# test host and port
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
)
async def test_setup_from_yaml_without_port_device_offline(hass: HomeAssistant):
@ -88,9 +82,6 @@ async def test_setup_from_yaml_without_port_device_offline(hass: HomeAssistant):
), patch(
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.device_info",
return_value=None,
), patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
@ -104,12 +95,8 @@ async def test_setup_from_yaml_without_port_device_online(
hass: HomeAssistant, remotews: Mock
):
"""Test import from yaml when the device is online."""
with patch(
"homeassistant.components.samsungtv.config_flow.socket.gethostbyname",
return_value="fake_host",
):
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
await hass.async_block_till_done()
config_entries_domain = hass.config_entries.async_entries(SAMSUNGTV_DOMAIN)
assert len(config_entries_domain) == 1
@ -118,13 +105,13 @@ async def test_setup_from_yaml_without_port_device_online(
async def test_setup_duplicate_config(hass: HomeAssistant, remote: Mock, caplog):
"""Test duplicate setup of platform."""
DUPLICATE = {
duplicate = {
SAMSUNGTV_DOMAIN: [
MOCK_CONFIG[SAMSUNGTV_DOMAIN][0],
MOCK_CONFIG[SAMSUNGTV_DOMAIN][0],
]
}
await async_setup_component(hass, SAMSUNGTV_DOMAIN, DUPLICATE)
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
@ -132,7 +119,7 @@ async def test_setup_duplicate_config(hass: HomeAssistant, remote: Mock, caplog)
async def test_setup_duplicate_entries(
hass: HomeAssistant, remote: Mock, remotews: Mock, no_mac_address: Mock, caplog
hass: HomeAssistant, remote: Mock, remotews: Mock, no_mac_address: Mock
):
"""Test duplicate setup of platform."""
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)

View file

@ -2,10 +2,11 @@
import asyncio
from datetime import timedelta
import logging
from unittest.mock import DEFAULT as DEFAULT_MOCK, Mock, PropertyMock, call, patch
from unittest.mock import DEFAULT as DEFAULT_MOCK, Mock, call, patch
import pytest
from samsungctl import exceptions
from samsungtvws import SamsungTVWS
from samsungtvws.exceptions import ConnectionFailure
from websocket import WebSocketException
@ -117,6 +118,9 @@ MOCK_CONFIG_NOTURNON = {
]
}
# Fake mac address in all mediaplayer tests.
pytestmark = pytest.mark.usefixtures("no_mac_address")
@pytest.fixture(name="delay")
def delay_fixture():
@ -127,11 +131,6 @@ def delay_fixture():
yield delay
@pytest.fixture(autouse=True)
def mock_no_mac_address(no_mac_address):
"""Fake mac address in all mediaplayer tests."""
async def setup_samsungtv(hass, config):
"""Set up mock Samsung TV."""
await async_setup_component(hass, SAMSUNGTV_DOMAIN, config)
@ -150,13 +149,11 @@ async def test_setup_without_turnon(hass, remote):
assert hass.states.get(ENTITY_ID_NOTURNON)
async def test_setup_websocket(hass, remotews, mock_now):
async def test_setup_websocket(hass, remotews):
"""Test setup of platform."""
with patch("homeassistant.components.samsungtv.bridge.SamsungTVWS") as remote_class:
enter = Mock()
type(enter).token = PropertyMock(return_value="987654321")
remote = Mock()
remote.__enter__ = Mock(return_value=enter)
remote = Mock(SamsungTVWS)
remote.__enter__ = Mock(return_value=remote)
remote.__exit__ = Mock()
remote.rest_device_info.return_value = {
"id": "uuid:be9554b9-c9fb-41f4-8920-22da015376a4",
@ -168,6 +165,7 @@ async def test_setup_websocket(hass, remotews, mock_now):
"networkType": "wireless",
},
}
remote.token = "123456789"
remote_class.return_value = remote
await setup_samsungtv(hass, MOCK_CONFIGWS)
@ -202,10 +200,8 @@ async def test_setup_websocket_2(hass, mock_now):
assert entry is config_entries[0]
with patch("homeassistant.components.samsungtv.bridge.SamsungTVWS") as remote_class:
enter = Mock()
type(enter).token = PropertyMock(return_value="987654321")
remote = Mock()
remote.__enter__ = Mock(return_value=enter)
remote = Mock(SamsungTVWS)
remote.__enter__ = Mock(return_value=remote)
remote.__exit__ = Mock()
remote.rest_device_info.return_value = {
"id": "uuid:be9554b9-c9fb-41f4-8920-22da015376a4",
@ -217,6 +213,7 @@ async def test_setup_websocket_2(hass, mock_now):
"networkType": "wireless",
},
}
remote.token = "987654321"
remote_class.return_value = remote
assert await async_setup_component(hass, SAMSUNGTV_DOMAIN, {})
await hass.async_block_till_done()
@ -742,7 +739,7 @@ async def test_play_media(hass, remote):
assert len(sleeps) == 3
async def test_play_media_invalid_type(hass, remote):
async def test_play_media_invalid_type(hass):
"""Test for play_media with invalid media type."""
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote:
url = "https://example.com"
@ -764,7 +761,7 @@ async def test_play_media_invalid_type(hass, remote):
assert remote.call_count == 1
async def test_play_media_channel_as_string(hass, remote):
async def test_play_media_channel_as_string(hass):
"""Test for play_media with invalid channel as string."""
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote:
url = "https://example.com"
@ -786,7 +783,7 @@ async def test_play_media_channel_as_string(hass, remote):
assert remote.call_count == 1
async def test_play_media_channel_as_non_positive(hass, remote):
async def test_play_media_channel_as_non_positive(hass):
"""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)
@ -823,7 +820,7 @@ async def test_select_source(hass, remote):
assert remote.close.call_args_list == [call()]
async def test_select_source_invalid_source(hass, remote):
async def test_select_source_invalid_source(hass):
"""Test for select_source with invalid source."""
with patch("homeassistant.components.samsungtv.bridge.Remote") as remote:
await setup_samsungtv(hass, MOCK_CONFIG)