mirror of
https://github.com/home-assistant/core
synced 2024-08-06 09:34:49 +02:00
Implement async websocket in samsungtv (#67127)
Co-authored-by: epenet <epenet@users.noreply.github.com>
This commit is contained in:
parent
b245ba6d57
commit
b8861578ff
@ -125,11 +125,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
entry, data={**entry.data, CONF_TOKEN: bridge.token}
|
||||
)
|
||||
|
||||
def new_token_callback() -> None:
|
||||
"""Update config entry with the new token."""
|
||||
hass.add_job(_update_token)
|
||||
|
||||
bridge.register_new_token_callback(new_token_callback)
|
||||
bridge.register_new_token_callback(_update_token)
|
||||
|
||||
async def stop_bridge(event: Event) -> None:
|
||||
"""Stop SamsungTV bridge connection."""
|
||||
|
@ -2,16 +2,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import asyncio
|
||||
from asyncio.exceptions import TimeoutError as AsyncioTimeoutError
|
||||
import contextlib
|
||||
from typing import Any, cast
|
||||
|
||||
from samsungctl import Remote
|
||||
from samsungctl.exceptions import AccessDenied, ConnectionClosed, UnhandledResponse
|
||||
from samsungtvws import SamsungTVWS
|
||||
from samsungtvws.async_remote import SamsungTVWSAsyncRemote
|
||||
from samsungtvws.async_rest import SamsungTVAsyncRest
|
||||
from samsungtvws.exceptions import ConnectionFailure, HttpApiError
|
||||
from websocket import WebSocketException
|
||||
from samsungtvws.remote import ChannelEmitCommand, SendRemoteKey
|
||||
from websockets.exceptions import WebSocketException
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
@ -298,7 +300,8 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||
self.token = token
|
||||
self._rest_api: SamsungTVAsyncRest | None = None
|
||||
self._app_list: dict[str, str] | None = None
|
||||
self._remote: SamsungTVWS | None = None
|
||||
self._remote: SamsungTVWSAsyncRemote | None = None
|
||||
self._remote_lock = asyncio.Lock()
|
||||
|
||||
async def async_mac_from_device(self) -> str | None:
|
||||
"""Try to fetch the mac address of the TV."""
|
||||
@ -306,39 +309,27 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||
return mac_from_device_info(info) if info else None
|
||||
|
||||
async def async_get_app_list(self) -> dict[str, str] | None:
|
||||
"""Get installed app list."""
|
||||
return await self.hass.async_add_executor_job(self._get_app_list)
|
||||
|
||||
def _get_app_list(self) -> dict[str, str] | None:
|
||||
"""Get installed app list."""
|
||||
if self._app_list is None:
|
||||
if remote := self._get_remote():
|
||||
raw_app_list = remote.app_list()
|
||||
if remote := await self._async_get_remote():
|
||||
raw_app_list = await remote.app_list()
|
||||
self._app_list = {
|
||||
app["name"]: app["appId"]
|
||||
for app in sorted(
|
||||
raw_app_list or [], key=lambda app: cast(str, app["name"])
|
||||
raw_app_list or [],
|
||||
key=lambda app: cast(str, app["name"]),
|
||||
)
|
||||
}
|
||||
|
||||
LOGGER.debug("Generated app list: %s", self._app_list)
|
||||
return self._app_list
|
||||
|
||||
async def async_is_on(self) -> bool:
|
||||
"""Tells if the TV is on."""
|
||||
return await self.hass.async_add_executor_job(self._is_on)
|
||||
|
||||
def _is_on(self) -> bool:
|
||||
"""Tells if the TV is on."""
|
||||
if self._remote is not None:
|
||||
self._close_remote()
|
||||
|
||||
return self._get_remote() is not None
|
||||
if remote := await self._async_get_remote():
|
||||
return remote.is_alive()
|
||||
return False
|
||||
|
||||
async def async_try_connect(self) -> str:
|
||||
"""Try to connect to the Websocket TV."""
|
||||
return await self.hass.async_add_executor_job(self._try_connect)
|
||||
|
||||
def _try_connect(self) -> str:
|
||||
"""Try to connect to the Websocket TV."""
|
||||
for self.port in WEBSOCKET_PORTS:
|
||||
config = {
|
||||
@ -353,14 +344,14 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||
result = None
|
||||
try:
|
||||
LOGGER.debug("Try config: %s", config)
|
||||
with SamsungTVWS(
|
||||
async with SamsungTVWSAsyncRemote(
|
||||
host=self.host,
|
||||
port=self.port,
|
||||
token=self.token,
|
||||
timeout=TIMEOUT_REQUEST,
|
||||
name=VALUE_CONF_NAME,
|
||||
) as remote:
|
||||
remote.open()
|
||||
await remote.open()
|
||||
self.token = remote.token
|
||||
LOGGER.debug("Working config: %s", config)
|
||||
return RESULT_SUCCESS
|
||||
@ -369,7 +360,7 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||
"Working but unsupported config: %s, error: %s", config, err
|
||||
)
|
||||
result = RESULT_NOT_SUPPORTED
|
||||
except (OSError, ConnectionFailure) as err:
|
||||
except (OSError, AsyncioTimeoutError, ConnectionFailure) as err:
|
||||
LOGGER.debug("Failing config: %s, error: %s", config, err)
|
||||
# pylint: disable=useless-else-on-loop
|
||||
else:
|
||||
@ -397,10 +388,6 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||
return None
|
||||
|
||||
async def async_send_key(self, key: str, key_type: str | None = None) -> None:
|
||||
"""Send the key using websocket protocol."""
|
||||
await self.hass.async_add_executor_job(self._send_key, key, key_type)
|
||||
|
||||
def _send_key(self, key: str, key_type: str | None = None) -> None:
|
||||
"""Send the key using websocket protocol."""
|
||||
if key == "KEY_POWEROFF":
|
||||
key = "KEY_POWER"
|
||||
@ -409,11 +396,13 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||
retry_count = 1
|
||||
for _ in range(retry_count + 1):
|
||||
try:
|
||||
if remote := self._get_remote():
|
||||
if remote := await self._async_get_remote():
|
||||
if key_type == "run_app":
|
||||
remote.run_app(key)
|
||||
await remote.send_command(
|
||||
ChannelEmitCommand.launch_app(key)
|
||||
)
|
||||
else:
|
||||
remote.send_key(key)
|
||||
await remote.send_command(SendRemoteKey.click(key))
|
||||
break
|
||||
except (
|
||||
BrokenPipeError,
|
||||
@ -426,29 +415,40 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||
# Different reasons, e.g. hostname not resolveable
|
||||
pass
|
||||
|
||||
def _get_remote(self) -> SamsungTVWS | None:
|
||||
async def _async_get_remote(self) -> SamsungTVWSAsyncRemote | None:
|
||||
"""Create or return a remote control instance."""
|
||||
if self._remote is None:
|
||||
if (remote := self._remote) and remote.is_alive():
|
||||
# If we have one then try to use it
|
||||
return remote
|
||||
|
||||
async with self._remote_lock:
|
||||
# If we don't have one make sure we do it under the lock
|
||||
# so we don't make two do due a race to get the remote
|
||||
return await self._async_get_remote_under_lock()
|
||||
|
||||
async def _async_get_remote_under_lock(self) -> SamsungTVWSAsyncRemote | None:
|
||||
"""Create or return a remote control instance."""
|
||||
if self._remote is None or not self._remote.is_alive():
|
||||
# We need to create a new instance to reconnect.
|
||||
try:
|
||||
LOGGER.debug(
|
||||
"Create SamsungTVWSBridge for %s (%s)", CONF_NAME, self.host
|
||||
)
|
||||
assert self.port
|
||||
self._remote = SamsungTVWS(
|
||||
self._remote = SamsungTVWSAsyncRemote(
|
||||
host=self.host,
|
||||
port=self.port,
|
||||
token=self.token,
|
||||
timeout=TIMEOUT_WEBSOCKET,
|
||||
name=VALUE_CONF_NAME,
|
||||
)
|
||||
self._remote.open()
|
||||
await self._remote.start_listening()
|
||||
# This is only happening when the auth was switched to DENY
|
||||
# A removed auth will lead to socket timeout because waiting for auth popup is just an open socket
|
||||
except ConnectionFailure as err:
|
||||
LOGGER.debug("ConnectionFailure %s", err.__repr__())
|
||||
self._notify_reauth_callback()
|
||||
except (WebSocketException, OSError) as err:
|
||||
except (WebSocketException, AsyncioTimeoutError, OSError) as err:
|
||||
LOGGER.debug("WebSocketException, OSError %s", err.__repr__())
|
||||
self._remote = None
|
||||
else:
|
||||
@ -465,15 +465,11 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||
return self._remote
|
||||
|
||||
async def async_close_remote(self) -> None:
|
||||
"""Close remote object."""
|
||||
await self.hass.async_add_executor_job(self._close_remote)
|
||||
|
||||
def _close_remote(self) -> None:
|
||||
"""Close remote object."""
|
||||
try:
|
||||
if self._remote is not None:
|
||||
# Close the current remote connection
|
||||
self._remote.close()
|
||||
await self._remote.close()
|
||||
self._remote = None
|
||||
except OSError:
|
||||
LOGGER.debug("Could not establish connection")
|
||||
|
@ -1,10 +1,10 @@
|
||||
"""Fixtures for Samsung TV."""
|
||||
from datetime import datetime
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
from samsungctl import Remote
|
||||
from samsungtvws import SamsungTVWS
|
||||
from samsungtvws.async_remote import SamsungTVWSAsyncRemote
|
||||
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
@ -56,11 +56,11 @@ def rest_api_fixture() -> Mock:
|
||||
def remotews_fixture() -> Mock:
|
||||
"""Patch the samsungtvws SamsungTVWS."""
|
||||
with patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote",
|
||||
) as remotews_class:
|
||||
remotews = Mock(SamsungTVWS)
|
||||
remotews.__enter__ = Mock(return_value=remotews)
|
||||
remotews.__exit__ = Mock()
|
||||
remotews = Mock(SamsungTVWSAsyncRemote)
|
||||
remotews.__aenter__ = AsyncMock(return_value=remotews)
|
||||
remotews.__aexit__ = AsyncMock()
|
||||
remotews.app_list.return_value = SAMPLE_APP_LIST
|
||||
remotews.token = "FAKE_TOKEN"
|
||||
remotews_class.return_value = remotews
|
||||
|
@ -4,9 +4,9 @@ from unittest.mock import ANY, AsyncMock, Mock, call, patch
|
||||
|
||||
import pytest
|
||||
from samsungctl.exceptions import AccessDenied, UnhandledResponse
|
||||
from samsungtvws import SamsungTVWS
|
||||
from samsungtvws.async_remote import SamsungTVWSAsyncRemote
|
||||
from samsungtvws.exceptions import ConnectionFailure, HttpApiError
|
||||
from websocket import WebSocketException, WebSocketProtocolException
|
||||
from websockets.exceptions import WebSocketException, WebSocketProtocolError
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import dhcp, ssdp, zeroconf
|
||||
@ -272,8 +272,8 @@ async def test_user_websocket_not_supported(hass: HomeAssistant) -> None:
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||
side_effect=WebSocketProtocolException("Boom"),
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote.open",
|
||||
side_effect=WebSocketProtocolError("Boom"),
|
||||
):
|
||||
# websocket device not supported
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@ -289,7 +289,7 @@ async def test_user_not_successful(hass: HomeAssistant) -> None:
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote.open",
|
||||
side_effect=OSError("Boom"),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@ -305,7 +305,7 @@ async def test_user_not_successful_2(hass: HomeAssistant) -> None:
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote.open",
|
||||
side_effect=ConnectionFailure("Boom"),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@ -464,9 +464,9 @@ async def test_ssdp_websocket_not_supported(
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS",
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote",
|
||||
) as remotews, patch.object(
|
||||
remotews, "open", side_effect=WebSocketProtocolException("Boom")
|
||||
remotews, "open", side_effect=WebSocketProtocolError("Boom")
|
||||
):
|
||||
# device not supported
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@ -497,7 +497,7 @@ async def test_ssdp_not_successful(hass: HomeAssistant) -> None:
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote.open",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||
@ -526,7 +526,7 @@ async def test_ssdp_not_successful_2(hass: HomeAssistant) -> None:
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote.open",
|
||||
side_effect=ConnectionFailure("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||
@ -830,13 +830,13 @@ async def test_autodetect_websocket(hass: HomeAssistant) -> None:
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote"
|
||||
) as remotews, patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVAsyncRest",
|
||||
) as rest_api_class:
|
||||
remote = Mock(SamsungTVWS)
|
||||
remote.__enter__ = Mock(return_value=remote)
|
||||
remote.__exit__ = Mock(return_value=False)
|
||||
remote = Mock(SamsungTVWSAsyncRemote)
|
||||
remote.__aenter__ = AsyncMock(return_value=remote)
|
||||
remote.__aexit__ = AsyncMock(return_value=False)
|
||||
remote.app_list.return_value = SAMPLE_APP_LIST
|
||||
rest_api_class.return_value.rest_device_info = AsyncMock(
|
||||
return_value={
|
||||
@ -876,15 +876,15 @@ async def test_websocket_no_mac(hass: HomeAssistant) -> None:
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS"
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote"
|
||||
) as remotews, patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVAsyncRest",
|
||||
) as rest_api_class, patch(
|
||||
"getmac.get_mac_address", return_value="gg:hh:ii:ll:mm:nn"
|
||||
):
|
||||
remote = Mock(SamsungTVWS)
|
||||
remote.__enter__ = Mock(return_value=remote)
|
||||
remote.__exit__ = Mock(return_value=False)
|
||||
remote = Mock(SamsungTVWSAsyncRemote)
|
||||
remote.__aenter__ = AsyncMock(return_value=remote)
|
||||
remote.__aexit__ = AsyncMock(return_value=False)
|
||||
remote.app_list.return_value = SAMPLE_APP_LIST
|
||||
rest_api_class.return_value.rest_device_info = AsyncMock(
|
||||
return_value={
|
||||
@ -964,15 +964,15 @@ async def test_autodetect_legacy(hass: HomeAssistant) -> None:
|
||||
async def test_autodetect_none(hass: HomeAssistant) -> None:
|
||||
"""Test for send key with autodetection of protocol."""
|
||||
mock_remotews = Mock()
|
||||
mock_remotews.__enter__ = Mock(return_value=mock_remotews)
|
||||
mock_remotews.__exit__ = Mock()
|
||||
mock_remotews.__aenter__ = AsyncMock(return_value=mock_remotews)
|
||||
mock_remotews.__aexit__ = AsyncMock()
|
||||
mock_remotews.open = Mock(side_effect=OSError("Boom"))
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.samsungtv.bridge.Remote",
|
||||
side_effect=OSError("Boom"),
|
||||
) as remote, patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS",
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote",
|
||||
return_value=mock_remotews,
|
||||
) as remotews:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@ -1314,7 +1314,7 @@ async def test_form_reauth_websocket_not_supported(hass: HomeAssistant) -> None:
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote.open",
|
||||
side_effect=WebSocketException,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
|
@ -80,7 +80,7 @@ async def test_setup_from_yaml_without_port_device_offline(hass: HomeAssistant)
|
||||
with patch(
|
||||
"homeassistant.components.samsungtv.bridge.Remote", side_effect=OSError
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote.open",
|
||||
side_effect=OSError,
|
||||
), patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||
|
@ -2,13 +2,14 @@
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from unittest.mock import DEFAULT as DEFAULT_MOCK, Mock, call, patch
|
||||
from unittest.mock import DEFAULT as DEFAULT_MOCK, AsyncMock, Mock, call, patch
|
||||
|
||||
import pytest
|
||||
from samsungctl import exceptions
|
||||
from samsungtvws import SamsungTVWS
|
||||
from samsungtvws.async_remote import SamsungTVWSAsyncRemote
|
||||
from samsungtvws.exceptions import ConnectionFailure
|
||||
from websocket import WebSocketException
|
||||
from samsungtvws.remote import ChannelEmitCommand, SendRemoteKey
|
||||
from websockets.exceptions import WebSocketException
|
||||
|
||||
from homeassistant.components.media_player import MediaPlayerDeviceClass
|
||||
from homeassistant.components.media_player.const import (
|
||||
@ -159,13 +160,13 @@ async def test_setup_without_turnon(hass: HomeAssistant) -> None:
|
||||
@pytest.mark.usefixtures("remotews")
|
||||
async def test_setup_websocket(hass: HomeAssistant) -> None:
|
||||
"""Test setup of platform."""
|
||||
|
||||
with patch("homeassistant.components.samsungtv.bridge.SamsungTVWS") as remote_class:
|
||||
remote = Mock(SamsungTVWS)
|
||||
remote.__enter__ = Mock(return_value=remote)
|
||||
remote.__exit__ = Mock()
|
||||
with patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote"
|
||||
) as remote_class:
|
||||
remote = Mock(SamsungTVWSAsyncRemote)
|
||||
remote.__aenter__ = AsyncMock(return_value=remote)
|
||||
remote.__aexit__ = AsyncMock()
|
||||
remote.app_list.return_value = SAMPLE_APP_LIST
|
||||
|
||||
remote.token = "123456789"
|
||||
remote_class.return_value = remote
|
||||
|
||||
@ -209,10 +210,12 @@ async def test_setup_websocket_2(
|
||||
"networkType": "wireless",
|
||||
},
|
||||
}
|
||||
with patch("homeassistant.components.samsungtv.bridge.SamsungTVWS") as remote_class:
|
||||
remote = Mock(SamsungTVWS)
|
||||
remote.__enter__ = Mock(return_value=remote)
|
||||
remote.__exit__ = Mock()
|
||||
with patch(
|
||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSAsyncRemote"
|
||||
) as remote_class:
|
||||
remote = Mock(SamsungTVWSAsyncRemote)
|
||||
remote.__aenter__ = AsyncMock(return_value=remote)
|
||||
remote.__aexit__ = AsyncMock()
|
||||
remote.app_list.return_value = SAMPLE_APP_LIST
|
||||
remote.token = "987654321"
|
||||
remote_class.return_value = remote
|
||||
@ -228,8 +231,7 @@ async def test_setup_websocket_2(
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert remote_class.call_count == 2
|
||||
assert remote_class.call_args_list[0] == call(**MOCK_CALLS_WS)
|
||||
remote_class.assert_called_once_with(**MOCK_CALLS_WS)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("remote")
|
||||
@ -274,7 +276,8 @@ async def test_update_off_ws(
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state.state == STATE_ON
|
||||
|
||||
remotews.open = Mock(side_effect=WebSocketException("Boom"))
|
||||
remotews.start_listening = Mock(side_effect=WebSocketException("Boom"))
|
||||
remotews.is_alive.return_value = False
|
||||
|
||||
next_update = mock_now + timedelta(minutes=5)
|
||||
with patch("homeassistant.util.dt.utcnow", return_value=next_update):
|
||||
@ -322,7 +325,9 @@ async def test_update_connection_failure(
|
||||
):
|
||||
await setup_samsungtv(hass, MOCK_CONFIGWS)
|
||||
|
||||
with patch.object(remotews, "open", side_effect=ConnectionFailure("Boom")):
|
||||
with patch.object(
|
||||
remotews, "start_listening", side_effect=ConnectionFailure("Boom")
|
||||
), patch.object(remotews, "is_alive", return_value=False):
|
||||
next_update = mock_now + timedelta(minutes=5)
|
||||
with patch("homeassistant.util.dt.utcnow", return_value=next_update):
|
||||
async_fire_time_changed(hass, next_update)
|
||||
@ -454,7 +459,7 @@ async def test_send_key_unhandled_response(hass: HomeAssistant, remote: Mock) ->
|
||||
async def test_send_key_websocketexception(hass: HomeAssistant, remotews: Mock) -> None:
|
||||
"""Testing unhandled response exception."""
|
||||
await setup_samsungtv(hass, MOCK_CONFIGWS)
|
||||
remotews.send_key = Mock(side_effect=WebSocketException("Boom"))
|
||||
remotews.send_command = Mock(side_effect=WebSocketException("Boom"))
|
||||
assert await hass.services.async_call(
|
||||
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
|
||||
)
|
||||
@ -465,7 +470,7 @@ async def test_send_key_websocketexception(hass: HomeAssistant, remotews: Mock)
|
||||
async def test_send_key_os_error_ws(hass: HomeAssistant, remotews: Mock) -> None:
|
||||
"""Testing unhandled response exception."""
|
||||
await setup_samsungtv(hass, MOCK_CONFIGWS)
|
||||
remotews.send_key = Mock(side_effect=OSError("Boom"))
|
||||
remotews.send_command = Mock(side_effect=OSError("Boom"))
|
||||
assert await hass.services.async_call(
|
||||
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
|
||||
)
|
||||
@ -572,18 +577,22 @@ async def test_turn_off_websocket(hass: HomeAssistant, remotews: Mock) -> None:
|
||||
side_effect=[OSError("Boom"), DEFAULT_MOCK],
|
||||
):
|
||||
await setup_samsungtv(hass, MOCK_CONFIGWS)
|
||||
remotews.send_command.reset_mock()
|
||||
|
||||
assert await hass.services.async_call(
|
||||
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True
|
||||
)
|
||||
# key called
|
||||
assert remotews.send_key.call_count == 1
|
||||
assert remotews.send_key.call_args_list == [call("KEY_POWER")]
|
||||
assert remotews.send_command.call_count == 1
|
||||
command = remotews.send_command.call_args_list[0].args[0]
|
||||
assert isinstance(command, SendRemoteKey)
|
||||
assert command.params["DataOfCmd"] == "KEY_POWER"
|
||||
|
||||
assert await hass.services.async_call(
|
||||
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID}, True
|
||||
)
|
||||
# key not called
|
||||
assert remotews.send_key.call_count == 1
|
||||
assert remotews.send_command.call_count == 1
|
||||
|
||||
|
||||
async def test_turn_off_legacy(hass: HomeAssistant, remote: Mock) -> None:
|
||||
@ -911,6 +920,7 @@ async def test_select_source_invalid_source(hass: HomeAssistant) -> None:
|
||||
async def test_play_media_app(hass: HomeAssistant, remotews: Mock) -> None:
|
||||
"""Test for play_media."""
|
||||
await setup_samsungtv(hass, MOCK_CONFIGWS)
|
||||
remotews.send_command.reset_mock()
|
||||
|
||||
assert await hass.services.async_call(
|
||||
DOMAIN,
|
||||
@ -922,18 +932,24 @@ async def test_play_media_app(hass: HomeAssistant, remotews: Mock) -> None:
|
||||
},
|
||||
True,
|
||||
)
|
||||
assert remotews.run_app.call_count == 1
|
||||
assert remotews.run_app.call_args_list == [call("3201608010191")]
|
||||
assert remotews.send_command.call_count == 1
|
||||
command = remotews.send_command.call_args_list[0].args[0]
|
||||
assert isinstance(command, ChannelEmitCommand)
|
||||
assert command.params["data"]["appId"] == "3201608010191"
|
||||
|
||||
|
||||
async def test_select_source_app(hass: HomeAssistant, remotews: Mock) -> None:
|
||||
"""Test for select_source."""
|
||||
await setup_samsungtv(hass, MOCK_CONFIGWS)
|
||||
remotews.send_command.reset_mock()
|
||||
|
||||
assert await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SELECT_SOURCE,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_INPUT_SOURCE: "Deezer"},
|
||||
True,
|
||||
)
|
||||
assert remotews.run_app.call_count == 1
|
||||
assert remotews.run_app.call_args_list == [call("3201608010191")]
|
||||
assert remotews.send_command.call_count == 1
|
||||
command = remotews.send_command.call_args_list[0].args[0]
|
||||
assert isinstance(command, ChannelEmitCommand)
|
||||
assert command.params["data"]["appId"] == "3201608010191"
|
||||
|
Loading…
Reference in New Issue
Block a user