Expose Samsung wrapper as async (#67042)
Co-authored-by: epenet <epenet@users.noreply.github.com>pull/67068/head
parent
d25a46d68d
commit
a60c37cdb8
|
@ -100,10 +100,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_get_device_bridge(
|
def _async_get_device_bridge(
|
||||||
data: dict[str, Any]
|
hass: HomeAssistant, data: dict[str, Any]
|
||||||
) -> SamsungTVLegacyBridge | SamsungTVWSBridge:
|
) -> SamsungTVLegacyBridge | SamsungTVWSBridge:
|
||||||
"""Get device bridge."""
|
"""Get device bridge."""
|
||||||
return SamsungTVBridge.get_bridge(
|
return SamsungTVBridge.get_bridge(
|
||||||
|
hass,
|
||||||
data[CONF_METHOD],
|
data[CONF_METHOD],
|
||||||
data[CONF_HOST],
|
data[CONF_HOST],
|
||||||
data[CONF_PORT],
|
data[CONF_PORT],
|
||||||
|
@ -131,9 +132,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
bridge.register_new_token_callback(new_token_callback)
|
bridge.register_new_token_callback(new_token_callback)
|
||||||
|
|
||||||
def stop_bridge(event: Event) -> None:
|
async def stop_bridge(event: Event) -> None:
|
||||||
"""Stop SamsungTV bridge connection."""
|
"""Stop SamsungTV bridge connection."""
|
||||||
bridge.stop()
|
await bridge.async_stop()
|
||||||
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_bridge)
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_bridge)
|
||||||
|
@ -169,14 +170,14 @@ async def _async_create_bridge_with_updated_data(
|
||||||
updated_data[CONF_PORT] = port
|
updated_data[CONF_PORT] = port
|
||||||
updated_data[CONF_METHOD] = method
|
updated_data[CONF_METHOD] = method
|
||||||
|
|
||||||
bridge = _async_get_device_bridge({**entry.data, **updated_data})
|
bridge = _async_get_device_bridge(hass, {**entry.data, **updated_data})
|
||||||
|
|
||||||
mac = entry.data.get(CONF_MAC)
|
mac = entry.data.get(CONF_MAC)
|
||||||
if not mac and bridge.method == METHOD_WEBSOCKET:
|
if not mac and bridge.method == METHOD_WEBSOCKET:
|
||||||
if info:
|
if info:
|
||||||
mac = mac_from_device_info(info)
|
mac = mac_from_device_info(info)
|
||||||
else:
|
else:
|
||||||
mac = await hass.async_add_executor_job(bridge.mac_from_device)
|
mac = await bridge.async_mac_from_device()
|
||||||
|
|
||||||
if not mac:
|
if not mac:
|
||||||
mac = await hass.async_add_executor_job(
|
mac = await hass.async_add_executor_job(
|
||||||
|
@ -196,7 +197,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if unload_ok:
|
if unload_ok:
|
||||||
hass.data[DOMAIN][entry.entry_id].stop()
|
await hass.data[DOMAIN][entry.entry_id].async_stop()
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -54,25 +54,18 @@ async def async_get_device_info(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
bridge: SamsungTVWSBridge | SamsungTVLegacyBridge | None,
|
bridge: SamsungTVWSBridge | SamsungTVLegacyBridge | None,
|
||||||
host: str,
|
host: str,
|
||||||
) -> tuple[int | None, str | None, dict[str, Any] | None]:
|
|
||||||
"""Fetch the port, method, and device info."""
|
|
||||||
return await hass.async_add_executor_job(_get_device_info, bridge, host)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_device_info(
|
|
||||||
bridge: SamsungTVWSBridge | SamsungTVLegacyBridge, host: str
|
|
||||||
) -> tuple[int | None, str | None, dict[str, Any] | None]:
|
) -> tuple[int | None, str | None, dict[str, Any] | None]:
|
||||||
"""Fetch the port, method, and device info."""
|
"""Fetch the port, method, and device info."""
|
||||||
if bridge and bridge.port:
|
if bridge and bridge.port:
|
||||||
return bridge.port, bridge.method, bridge.device_info()
|
return bridge.port, bridge.method, await bridge.async_device_info()
|
||||||
|
|
||||||
for port in WEBSOCKET_PORTS:
|
for port in WEBSOCKET_PORTS:
|
||||||
bridge = SamsungTVBridge.get_bridge(METHOD_WEBSOCKET, host, port)
|
bridge = SamsungTVBridge.get_bridge(hass, METHOD_WEBSOCKET, host, port)
|
||||||
if info := bridge.device_info():
|
if info := await bridge.async_device_info():
|
||||||
return port, METHOD_WEBSOCKET, info
|
return port, METHOD_WEBSOCKET, info
|
||||||
|
|
||||||
bridge = SamsungTVBridge.get_bridge(METHOD_LEGACY, host, LEGACY_PORT)
|
bridge = SamsungTVBridge.get_bridge(hass, METHOD_LEGACY, host, LEGACY_PORT)
|
||||||
result = bridge.try_connect()
|
result = await bridge.async_try_connect()
|
||||||
if result in (RESULT_SUCCESS, RESULT_AUTH_MISSING):
|
if result in (RESULT_SUCCESS, RESULT_AUTH_MISSING):
|
||||||
return LEGACY_PORT, METHOD_LEGACY, None
|
return LEGACY_PORT, METHOD_LEGACY, None
|
||||||
|
|
||||||
|
@ -84,15 +77,22 @@ class SamsungTVBridge(ABC):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_bridge(
|
def get_bridge(
|
||||||
method: str, host: str, port: int | None = None, token: str | None = None
|
hass: HomeAssistant,
|
||||||
|
method: str,
|
||||||
|
host: str,
|
||||||
|
port: int | None = None,
|
||||||
|
token: str | None = None,
|
||||||
) -> SamsungTVLegacyBridge | SamsungTVWSBridge:
|
) -> SamsungTVLegacyBridge | SamsungTVWSBridge:
|
||||||
"""Get Bridge instance."""
|
"""Get Bridge instance."""
|
||||||
if method == METHOD_LEGACY or port == LEGACY_PORT:
|
if method == METHOD_LEGACY or port == LEGACY_PORT:
|
||||||
return SamsungTVLegacyBridge(method, host, port)
|
return SamsungTVLegacyBridge(hass, method, host, port)
|
||||||
return SamsungTVWSBridge(method, host, port, token)
|
return SamsungTVWSBridge(hass, method, host, port, token)
|
||||||
|
|
||||||
def __init__(self, method: str, host: str, port: int | None = None) -> None:
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, method: str, host: str, port: int | None = None
|
||||||
|
) -> None:
|
||||||
"""Initialize Bridge."""
|
"""Initialize Bridge."""
|
||||||
|
self.hass = hass
|
||||||
self.port = port
|
self.port = port
|
||||||
self.method = method
|
self.method = method
|
||||||
self.host = host
|
self.host = host
|
||||||
|
@ -110,28 +110,29 @@ class SamsungTVBridge(ABC):
|
||||||
self._new_token_callback = func
|
self._new_token_callback = func
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def try_connect(self) -> str | None:
|
async def async_try_connect(self) -> str | None:
|
||||||
"""Try to connect to the TV."""
|
"""Try to connect to the TV."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def device_info(self) -> dict[str, Any] | None:
|
async def async_device_info(self) -> dict[str, Any] | None:
|
||||||
"""Try to gather infos of this TV."""
|
"""Try to gather infos of this TV."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def mac_from_device(self) -> str | None:
|
async def async_mac_from_device(self) -> str | None:
|
||||||
"""Try to fetch the mac address of the TV."""
|
"""Try to fetch the mac address of the TV."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_app_list(self) -> dict[str, str] | None:
|
async def async_get_app_list(self) -> dict[str, str] | None:
|
||||||
"""Get installed app list."""
|
"""Get installed app list."""
|
||||||
|
|
||||||
def is_on(self) -> bool:
|
async def async_is_on(self) -> bool:
|
||||||
"""Tells if the TV is on."""
|
"""Tells if the TV is on."""
|
||||||
if self._remote is not None:
|
if self._remote is not None:
|
||||||
self.close_remote()
|
await self.async_close_remote()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self._get_remote() is not None
|
remote = await self.hass.async_add_executor_job(self._get_remote)
|
||||||
|
return remote is not None
|
||||||
except (
|
except (
|
||||||
UnhandledResponse,
|
UnhandledResponse,
|
||||||
AccessDenied,
|
AccessDenied,
|
||||||
|
@ -143,14 +144,14 @@ class SamsungTVBridge(ABC):
|
||||||
# Different reasons, e.g. hostname not resolveable
|
# Different reasons, e.g. hostname not resolveable
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def send_key(self, key: str, key_type: str | None = None) -> None:
|
async def async_send_key(self, key: str, key_type: str | None = None) -> None:
|
||||||
"""Send a key to the tv and handles exceptions."""
|
"""Send a key to the tv and handles exceptions."""
|
||||||
try:
|
try:
|
||||||
# recreate connection if connection was dead
|
# recreate connection if connection was dead
|
||||||
retry_count = 1
|
retry_count = 1
|
||||||
for _ in range(retry_count + 1):
|
for _ in range(retry_count + 1):
|
||||||
try:
|
try:
|
||||||
self._send_key(key, key_type)
|
await self._async_send_key(key, key_type)
|
||||||
break
|
break
|
||||||
except (
|
except (
|
||||||
ConnectionClosed,
|
ConnectionClosed,
|
||||||
|
@ -168,19 +169,19 @@ class SamsungTVBridge(ABC):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def _send_key(self, key: str, key_type: str | None = None) -> None:
|
async def _async_send_key(self, key: str, key_type: str | None = None) -> None:
|
||||||
"""Send the key."""
|
"""Send the key."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def _get_remote(self, avoid_open: bool = False) -> Remote:
|
def _get_remote(self, avoid_open: bool = False) -> Remote | SamsungTVWS:
|
||||||
"""Get Remote object."""
|
"""Get Remote object."""
|
||||||
|
|
||||||
def close_remote(self) -> None:
|
async def async_close_remote(self) -> None:
|
||||||
"""Close remote object."""
|
"""Close remote object."""
|
||||||
try:
|
try:
|
||||||
if self._remote is not None:
|
if self._remote is not None:
|
||||||
# Close the current remote connection
|
# Close the current remote connection
|
||||||
self._remote.close()
|
await self.hass.async_add_executor_job(self._remote.close)
|
||||||
self._remote = None
|
self._remote = None
|
||||||
except OSError:
|
except OSError:
|
||||||
LOGGER.debug("Could not establish connection")
|
LOGGER.debug("Could not establish connection")
|
||||||
|
@ -199,9 +200,11 @@ class SamsungTVBridge(ABC):
|
||||||
class SamsungTVLegacyBridge(SamsungTVBridge):
|
class SamsungTVLegacyBridge(SamsungTVBridge):
|
||||||
"""The Bridge for Legacy TVs."""
|
"""The Bridge for Legacy TVs."""
|
||||||
|
|
||||||
def __init__(self, method: str, host: str, port: int | None) -> None:
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, method: str, host: str, port: int | None
|
||||||
|
) -> None:
|
||||||
"""Initialize Bridge."""
|
"""Initialize Bridge."""
|
||||||
super().__init__(method, host, LEGACY_PORT)
|
super().__init__(hass, method, host, LEGACY_PORT)
|
||||||
self.config = {
|
self.config = {
|
||||||
CONF_NAME: VALUE_CONF_NAME,
|
CONF_NAME: VALUE_CONF_NAME,
|
||||||
CONF_DESCRIPTION: VALUE_CONF_NAME,
|
CONF_DESCRIPTION: VALUE_CONF_NAME,
|
||||||
|
@ -212,15 +215,19 @@ class SamsungTVLegacyBridge(SamsungTVBridge):
|
||||||
CONF_TIMEOUT: 1,
|
CONF_TIMEOUT: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
def mac_from_device(self) -> None:
|
async def async_mac_from_device(self) -> None:
|
||||||
"""Try to fetch the mac address of the TV."""
|
"""Try to fetch the mac address of the TV."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_app_list(self) -> dict[str, str]:
|
async def async_get_app_list(self) -> dict[str, str]:
|
||||||
"""Get installed app list."""
|
"""Get installed app list."""
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def try_connect(self) -> str:
|
async def async_try_connect(self) -> str:
|
||||||
|
"""Try to connect to the Legacy TV."""
|
||||||
|
return await self.hass.async_add_executor_job(self._try_connect)
|
||||||
|
|
||||||
|
def _try_connect(self) -> str:
|
||||||
"""Try to connect to the Legacy TV."""
|
"""Try to connect to the Legacy TV."""
|
||||||
config = {
|
config = {
|
||||||
CONF_NAME: VALUE_CONF_NAME,
|
CONF_NAME: VALUE_CONF_NAME,
|
||||||
|
@ -247,7 +254,7 @@ class SamsungTVLegacyBridge(SamsungTVBridge):
|
||||||
LOGGER.debug("Failing config: %s, error: %s", config, err)
|
LOGGER.debug("Failing config: %s, error: %s", config, err)
|
||||||
return RESULT_CANNOT_CONNECT
|
return RESULT_CANNOT_CONNECT
|
||||||
|
|
||||||
def device_info(self) -> None:
|
async def async_device_info(self) -> None:
|
||||||
"""Try to gather infos of this device."""
|
"""Try to gather infos of this device."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -269,34 +276,47 @@ class SamsungTVLegacyBridge(SamsungTVBridge):
|
||||||
pass
|
pass
|
||||||
return self._remote
|
return self._remote
|
||||||
|
|
||||||
def _send_key(self, key: str, key_type: str | None = None) -> None:
|
async def _async_send_key(self, key: str, key_type: str | None = None) -> None:
|
||||||
|
"""Send the key using legacy protocol."""
|
||||||
|
return await self.hass.async_add_executor_job(self._send_key, key)
|
||||||
|
|
||||||
|
def _send_key(self, key: str) -> None:
|
||||||
"""Send the key using legacy protocol."""
|
"""Send the key using legacy protocol."""
|
||||||
if remote := self._get_remote():
|
if remote := self._get_remote():
|
||||||
remote.control(key)
|
remote.control(key)
|
||||||
|
|
||||||
def stop(self) -> None:
|
async def async_stop(self) -> None:
|
||||||
"""Stop Bridge."""
|
"""Stop Bridge."""
|
||||||
LOGGER.debug("Stopping SamsungTVLegacyBridge")
|
LOGGER.debug("Stopping SamsungTVLegacyBridge")
|
||||||
self.close_remote()
|
await self.async_close_remote()
|
||||||
|
|
||||||
|
|
||||||
class SamsungTVWSBridge(SamsungTVBridge):
|
class SamsungTVWSBridge(SamsungTVBridge):
|
||||||
"""The Bridge for WebSocket TVs."""
|
"""The Bridge for WebSocket TVs."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, method: str, host: str, port: int | None = None, token: str | None = None
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
method: str,
|
||||||
|
host: str,
|
||||||
|
port: int | None = None,
|
||||||
|
token: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize Bridge."""
|
"""Initialize Bridge."""
|
||||||
super().__init__(method, host, port)
|
super().__init__(hass, method, host, port)
|
||||||
self.token = token
|
self.token = token
|
||||||
self._app_list: dict[str, str] | None = None
|
self._app_list: dict[str, str] | None = None
|
||||||
|
|
||||||
def mac_from_device(self) -> str | None:
|
async def async_mac_from_device(self) -> str | None:
|
||||||
"""Try to fetch the mac address of the TV."""
|
"""Try to fetch the mac address of the TV."""
|
||||||
info = self.device_info()
|
info = await self.async_device_info()
|
||||||
return mac_from_device_info(info) if info else None
|
return mac_from_device_info(info) if info else None
|
||||||
|
|
||||||
def get_app_list(self) -> dict[str, str] | 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."""
|
"""Get installed app list."""
|
||||||
if self._app_list is None:
|
if self._app_list is None:
|
||||||
if remote := self._get_remote():
|
if remote := self._get_remote():
|
||||||
|
@ -308,7 +328,11 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||||
|
|
||||||
return self._app_list
|
return self._app_list
|
||||||
|
|
||||||
def try_connect(self) -> str:
|
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."""
|
"""Try to connect to the Websocket TV."""
|
||||||
for self.port in WEBSOCKET_PORTS:
|
for self.port in WEBSOCKET_PORTS:
|
||||||
config = {
|
config = {
|
||||||
|
@ -350,15 +374,21 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||||
|
|
||||||
return RESULT_CANNOT_CONNECT
|
return RESULT_CANNOT_CONNECT
|
||||||
|
|
||||||
def device_info(self) -> dict[str, Any] | None:
|
async def async_device_info(self) -> dict[str, Any] | None:
|
||||||
"""Try to gather infos of this TV."""
|
"""Try to gather infos of this TV."""
|
||||||
if remote := self._get_remote(avoid_open=True):
|
if remote := self._get_remote(avoid_open=True):
|
||||||
with contextlib.suppress(HttpApiError, RequestsTimeout):
|
with contextlib.suppress(HttpApiError, RequestsTimeout):
|
||||||
device_info: dict[str, Any] = remote.rest_device_info()
|
device_info: dict[str, Any] = await self.hass.async_add_executor_job(
|
||||||
|
remote.rest_device_info
|
||||||
|
)
|
||||||
return device_info
|
return device_info
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
async def _async_send_key(self, key: str, key_type: str | None = None) -> None:
|
||||||
|
"""Send the key using websocket protocol."""
|
||||||
|
return 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:
|
def _send_key(self, key: str, key_type: str | None = None) -> None:
|
||||||
"""Send the key using websocket protocol."""
|
"""Send the key using websocket protocol."""
|
||||||
if key == "KEY_POWEROFF":
|
if key == "KEY_POWEROFF":
|
||||||
|
@ -369,7 +399,7 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||||
else:
|
else:
|
||||||
remote.send_key(key)
|
remote.send_key(key)
|
||||||
|
|
||||||
def _get_remote(self, avoid_open: bool = False) -> Remote:
|
def _get_remote(self, avoid_open: bool = False) -> SamsungTVWS:
|
||||||
"""Create or return a remote control instance."""
|
"""Create or return a remote control instance."""
|
||||||
if self._remote is None:
|
if self._remote is None:
|
||||||
# We need to create a new instance to reconnect.
|
# We need to create a new instance to reconnect.
|
||||||
|
@ -388,11 +418,16 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||||
self._remote.open("samsung.remote.control")
|
self._remote.open("samsung.remote.control")
|
||||||
# This is only happening when the auth was switched to DENY
|
# 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
|
# A removed auth will lead to socket timeout because waiting for auth popup is just an open socket
|
||||||
except ConnectionFailure:
|
except ConnectionFailure as err:
|
||||||
|
LOGGER.debug("ConnectionFailure %s", err.__repr__())
|
||||||
self._notify_reauth_callback()
|
self._notify_reauth_callback()
|
||||||
except (WebSocketException, OSError):
|
except (WebSocketException, OSError) as err:
|
||||||
|
LOGGER.debug("WebSocketException, OSError %s", err.__repr__())
|
||||||
self._remote = None
|
self._remote = None
|
||||||
else:
|
else:
|
||||||
|
LOGGER.debug(
|
||||||
|
"Created SamsungTVWSBridge for %s (%s)", CONF_NAME, self.host
|
||||||
|
)
|
||||||
if self.token != self._remote.token:
|
if self.token != self._remote.token:
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"SamsungTVWSBridge has provided a new token %s",
|
"SamsungTVWSBridge has provided a new token %s",
|
||||||
|
@ -402,7 +437,7 @@ class SamsungTVWSBridge(SamsungTVBridge):
|
||||||
self._notify_new_token_callback()
|
self._notify_new_token_callback()
|
||||||
return self._remote
|
return self._remote
|
||||||
|
|
||||||
def stop(self) -> None:
|
async def async_stop(self) -> None:
|
||||||
"""Stop Bridge."""
|
"""Stop Bridge."""
|
||||||
LOGGER.debug("Stopping SamsungTVWSBridge")
|
LOGGER.debug("Stopping SamsungTVWSBridge")
|
||||||
self.close_remote()
|
await self.async_close_remote()
|
||||||
|
|
|
@ -124,11 +124,11 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
updates[CONF_MAC] = self._mac
|
updates[CONF_MAC] = self._mac
|
||||||
self._abort_if_unique_id_configured(updates=updates)
|
self._abort_if_unique_id_configured(updates=updates)
|
||||||
|
|
||||||
def _try_connect(self) -> None:
|
async def _try_connect(self) -> None:
|
||||||
"""Try to connect and check auth."""
|
"""Try to connect and check auth."""
|
||||||
for method in SUPPORTED_METHODS:
|
for method in SUPPORTED_METHODS:
|
||||||
self._bridge = SamsungTVBridge.get_bridge(method, self._host)
|
self._bridge = SamsungTVBridge.get_bridge(self.hass, method, self._host)
|
||||||
result = self._bridge.try_connect()
|
result = await self._bridge.async_try_connect()
|
||||||
if result == RESULT_SUCCESS:
|
if result == RESULT_SUCCESS:
|
||||||
return
|
return
|
||||||
if result != RESULT_CANNOT_CONNECT:
|
if result != RESULT_CANNOT_CONNECT:
|
||||||
|
@ -203,7 +203,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a flow initialized by the user."""
|
"""Handle a flow initialized by the user."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
await self._async_set_name_host_from_input(user_input)
|
await self._async_set_name_host_from_input(user_input)
|
||||||
await self.hass.async_add_executor_job(self._try_connect)
|
await self._try_connect()
|
||||||
assert self._bridge
|
assert self._bridge
|
||||||
self._async_abort_entries_match({CONF_HOST: self._host})
|
self._async_abort_entries_match({CONF_HOST: self._host})
|
||||||
if self._bridge.method != METHOD_LEGACY:
|
if self._bridge.method != METHOD_LEGACY:
|
||||||
|
@ -309,7 +309,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle user-confirmation of discovered node."""
|
"""Handle user-confirmation of discovered node."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
|
||||||
await self.hass.async_add_executor_job(self._try_connect)
|
await self._try_connect()
|
||||||
assert self._bridge
|
assert self._bridge
|
||||||
return self._get_entry_from_bridge()
|
return self._get_entry_from_bridge()
|
||||||
|
|
||||||
|
@ -341,9 +341,11 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
assert self._reauth_entry
|
assert self._reauth_entry
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
bridge = SamsungTVBridge.get_bridge(
|
bridge = SamsungTVBridge.get_bridge(
|
||||||
self._reauth_entry.data[CONF_METHOD], self._reauth_entry.data[CONF_HOST]
|
self.hass,
|
||||||
|
self._reauth_entry.data[CONF_METHOD],
|
||||||
|
self._reauth_entry.data[CONF_HOST],
|
||||||
)
|
)
|
||||||
result = await self.hass.async_add_executor_job(bridge.try_connect)
|
result = await bridge.async_try_connect()
|
||||||
if result == RESULT_SUCCESS:
|
if result == RESULT_SUCCESS:
|
||||||
new_data = dict(self._reauth_entry.data)
|
new_data = dict(self._reauth_entry.data)
|
||||||
new_data[CONF_TOKEN] = bridge.token
|
new_data[CONF_TOKEN] = bridge.token
|
||||||
|
|
|
@ -23,5 +23,5 @@ async def async_get_config_entry_diagnostics(
|
||||||
]
|
]
|
||||||
return {
|
return {
|
||||||
"entry": async_redact_data(entry.as_dict(), TO_REDACT),
|
"entry": async_redact_data(entry.as_dict(), TO_REDACT),
|
||||||
"device_info": await hass.async_add_executor_job(bridge.device_info),
|
"device_info": await bridge.async_device_info(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,30 +153,32 @@ class SamsungTVDevice(MediaPlayerEntity):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self) -> None:
|
async def async_update(self) -> None:
|
||||||
"""Update state of device."""
|
"""Update state of device."""
|
||||||
if self._auth_failed or self.hass.is_stopping:
|
if self._auth_failed or self.hass.is_stopping:
|
||||||
return
|
return
|
||||||
if self._power_off_in_progress():
|
if self._power_off_in_progress():
|
||||||
self._attr_state = STATE_OFF
|
self._attr_state = STATE_OFF
|
||||||
else:
|
else:
|
||||||
self._attr_state = STATE_ON if self._bridge.is_on() else STATE_OFF
|
self._attr_state = (
|
||||||
|
STATE_ON if await self._bridge.async_is_on() else STATE_OFF
|
||||||
|
)
|
||||||
|
|
||||||
if self._attr_state == STATE_ON and self._app_list is None:
|
if self._attr_state == STATE_ON and self._app_list is None:
|
||||||
self._app_list = {} # Ensure that we don't update it twice in parallel
|
self._app_list = {} # Ensure that we don't update it twice in parallel
|
||||||
self._update_app_list()
|
await self._async_update_app_list()
|
||||||
|
|
||||||
def _update_app_list(self) -> None:
|
async def _async_update_app_list(self) -> None:
|
||||||
self._app_list = self._bridge.get_app_list()
|
self._app_list = await self._bridge.async_get_app_list()
|
||||||
if self._app_list is not None:
|
if self._app_list is not None:
|
||||||
self._attr_source_list.extend(self._app_list)
|
self._attr_source_list.extend(self._app_list)
|
||||||
|
|
||||||
def send_key(self, key: str, key_type: str | None = None) -> None:
|
async def _async_send_key(self, key: str, key_type: str | None = None) -> None:
|
||||||
"""Send a key to the tv and handles exceptions."""
|
"""Send a key to the tv and handles exceptions."""
|
||||||
if self._power_off_in_progress() and key != "KEY_POWEROFF":
|
if self._power_off_in_progress() and key != "KEY_POWEROFF":
|
||||||
LOGGER.info("TV is powering off, not sending command: %s", key)
|
LOGGER.info("TV is powering off, not sending command: %s", key)
|
||||||
return
|
return
|
||||||
self._bridge.send_key(key, key_type)
|
await self._bridge.async_send_key(key, key_type)
|
||||||
|
|
||||||
def _power_off_in_progress(self) -> bool:
|
def _power_off_in_progress(self) -> bool:
|
||||||
return (
|
return (
|
||||||
|
@ -196,57 +198,57 @@ class SamsungTVDevice(MediaPlayerEntity):
|
||||||
or self._power_off_in_progress()
|
or self._power_off_in_progress()
|
||||||
)
|
)
|
||||||
|
|
||||||
def turn_off(self) -> None:
|
async def async_turn_off(self) -> None:
|
||||||
"""Turn off media player."""
|
"""Turn off media player."""
|
||||||
self._end_of_power_off = dt_util.utcnow() + SCAN_INTERVAL_PLUS_OFF_TIME
|
self._end_of_power_off = dt_util.utcnow() + SCAN_INTERVAL_PLUS_OFF_TIME
|
||||||
|
|
||||||
self.send_key("KEY_POWEROFF")
|
await self._async_send_key("KEY_POWEROFF")
|
||||||
# Force closing of remote session to provide instant UI feedback
|
# Force closing of remote session to provide instant UI feedback
|
||||||
self._bridge.close_remote()
|
await self._bridge.async_close_remote()
|
||||||
|
|
||||||
def volume_up(self) -> None:
|
async def async_volume_up(self) -> None:
|
||||||
"""Volume up the media player."""
|
"""Volume up the media player."""
|
||||||
self.send_key("KEY_VOLUP")
|
await self._async_send_key("KEY_VOLUP")
|
||||||
|
|
||||||
def volume_down(self) -> None:
|
async def async_volume_down(self) -> None:
|
||||||
"""Volume down media player."""
|
"""Volume down media player."""
|
||||||
self.send_key("KEY_VOLDOWN")
|
await self._async_send_key("KEY_VOLDOWN")
|
||||||
|
|
||||||
def mute_volume(self, mute: bool) -> None:
|
async def async_mute_volume(self, mute: bool) -> None:
|
||||||
"""Send mute command."""
|
"""Send mute command."""
|
||||||
self.send_key("KEY_MUTE")
|
await self._async_send_key("KEY_MUTE")
|
||||||
|
|
||||||
def media_play_pause(self) -> None:
|
async def async_media_play_pause(self) -> None:
|
||||||
"""Simulate play pause media player."""
|
"""Simulate play pause media player."""
|
||||||
if self._playing:
|
if self._playing:
|
||||||
self.media_pause()
|
await self.async_media_pause()
|
||||||
else:
|
else:
|
||||||
self.media_play()
|
await self.async_media_play()
|
||||||
|
|
||||||
def media_play(self) -> None:
|
async def async_media_play(self) -> None:
|
||||||
"""Send play command."""
|
"""Send play command."""
|
||||||
self._playing = True
|
self._playing = True
|
||||||
self.send_key("KEY_PLAY")
|
await self._async_send_key("KEY_PLAY")
|
||||||
|
|
||||||
def media_pause(self) -> None:
|
async def async_media_pause(self) -> None:
|
||||||
"""Send media pause command to media player."""
|
"""Send media pause command to media player."""
|
||||||
self._playing = False
|
self._playing = False
|
||||||
self.send_key("KEY_PAUSE")
|
await self._async_send_key("KEY_PAUSE")
|
||||||
|
|
||||||
def media_next_track(self) -> None:
|
async def async_media_next_track(self) -> None:
|
||||||
"""Send next track command."""
|
"""Send next track command."""
|
||||||
self.send_key("KEY_CHUP")
|
await self._async_send_key("KEY_CHUP")
|
||||||
|
|
||||||
def media_previous_track(self) -> None:
|
async def async_media_previous_track(self) -> None:
|
||||||
"""Send the previous track command."""
|
"""Send the previous track command."""
|
||||||
self.send_key("KEY_CHDOWN")
|
await self._async_send_key("KEY_CHDOWN")
|
||||||
|
|
||||||
async def async_play_media(
|
async def async_play_media(
|
||||||
self, media_type: str, media_id: str, **kwargs: Any
|
self, media_type: str, media_id: str, **kwargs: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Support changing a channel."""
|
"""Support changing a channel."""
|
||||||
if media_type == MEDIA_TYPE_APP:
|
if media_type == MEDIA_TYPE_APP:
|
||||||
await self.hass.async_add_executor_job(self.send_key, media_id, "run_app")
|
await self._async_send_key(media_id, "run_app")
|
||||||
return
|
return
|
||||||
|
|
||||||
if media_type != MEDIA_TYPE_CHANNEL:
|
if media_type != MEDIA_TYPE_CHANNEL:
|
||||||
|
@ -261,9 +263,9 @@ class SamsungTVDevice(MediaPlayerEntity):
|
||||||
return
|
return
|
||||||
|
|
||||||
for digit in media_id:
|
for digit in media_id:
|
||||||
await self.hass.async_add_executor_job(self.send_key, f"KEY_{digit}")
|
await self._async_send_key(f"KEY_{digit}")
|
||||||
await asyncio.sleep(KEY_PRESS_TIMEOUT)
|
await asyncio.sleep(KEY_PRESS_TIMEOUT)
|
||||||
await self.hass.async_add_executor_job(self.send_key, "KEY_ENTER")
|
await self._async_send_key("KEY_ENTER")
|
||||||
|
|
||||||
def _wake_on_lan(self) -> None:
|
def _wake_on_lan(self) -> None:
|
||||||
"""Wake the device via wake on lan."""
|
"""Wake the device via wake on lan."""
|
||||||
|
@ -279,14 +281,14 @@ class SamsungTVDevice(MediaPlayerEntity):
|
||||||
elif self._mac:
|
elif self._mac:
|
||||||
await self.hass.async_add_executor_job(self._wake_on_lan)
|
await self.hass.async_add_executor_job(self._wake_on_lan)
|
||||||
|
|
||||||
def select_source(self, source: str) -> None:
|
async def async_select_source(self, source: str) -> None:
|
||||||
"""Select input source."""
|
"""Select input source."""
|
||||||
if self._app_list and source in self._app_list:
|
if self._app_list and source in self._app_list:
|
||||||
self.send_key(self._app_list[source], "run_app")
|
await self._async_send_key(self._app_list[source], "run_app")
|
||||||
return
|
return
|
||||||
|
|
||||||
if source in SOURCES:
|
if source in SOURCES:
|
||||||
self.send_key(SOURCES[source])
|
await self._async_send_key(SOURCES[source])
|
||||||
return
|
return
|
||||||
|
|
||||||
LOGGER.error("Unsupported source")
|
LOGGER.error("Unsupported source")
|
||||||
|
|
|
@ -322,7 +322,7 @@ async def test_ssdp(hass: HomeAssistant, no_mac_address: Mock) -> None:
|
||||||
|
|
||||||
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
|
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.device_info",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||||
return_value=MOCK_DEVICE_INFO,
|
return_value=MOCK_DEVICE_INFO,
|
||||||
):
|
):
|
||||||
# confirm to add the entry
|
# confirm to add the entry
|
||||||
|
@ -351,7 +351,7 @@ async def test_ssdp_noprefix(hass: HomeAssistant, no_mac_address: Mock) -> None:
|
||||||
|
|
||||||
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
|
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.device_info",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||||
return_value=MOCK_DEVICE_INFO_2,
|
return_value=MOCK_DEVICE_INFO_2,
|
||||||
):
|
):
|
||||||
# confirm to add the entry
|
# confirm to add the entry
|
||||||
|
@ -399,7 +399,7 @@ async def test_ssdp_legacy_missing_auth(hass: HomeAssistant) -> None:
|
||||||
# missing authentication
|
# missing authentication
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVLegacyBridge.try_connect",
|
"homeassistant.components.samsungtv.bridge.SamsungTVLegacyBridge.async_try_connect",
|
||||||
return_value=RESULT_AUTH_MISSING,
|
return_value=RESULT_AUTH_MISSING,
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
@ -421,7 +421,7 @@ async def test_ssdp_legacy_not_supported(hass: HomeAssistant) -> None:
|
||||||
assert result["step_id"] == "confirm"
|
assert result["step_id"] == "confirm"
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVLegacyBridge.try_connect",
|
"homeassistant.components.samsungtv.bridge.SamsungTVLegacyBridge.async_try_connect",
|
||||||
return_value=RESULT_NOT_SUPPORTED,
|
return_value=RESULT_NOT_SUPPORTED,
|
||||||
):
|
):
|
||||||
# device not supported
|
# device not supported
|
||||||
|
@ -498,7 +498,7 @@ async def test_ssdp_not_successful(hass: HomeAssistant) -> None:
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||||
side_effect=OSError("Boom"),
|
side_effect=OSError("Boom"),
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.device_info",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||||
return_value=MOCK_DEVICE_INFO,
|
return_value=MOCK_DEVICE_INFO,
|
||||||
):
|
):
|
||||||
|
|
||||||
|
@ -527,7 +527,7 @@ async def test_ssdp_not_successful_2(hass: HomeAssistant) -> None:
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||||
side_effect=ConnectionFailure("Boom"),
|
side_effect=ConnectionFailure("Boom"),
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.device_info",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||||
return_value=MOCK_DEVICE_INFO,
|
return_value=MOCK_DEVICE_INFO,
|
||||||
):
|
):
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ async def test_ssdp_already_in_progress(
|
||||||
|
|
||||||
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
|
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.device_info",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||||
return_value=MOCK_DEVICE_INFO,
|
return_value=MOCK_DEVICE_INFO,
|
||||||
):
|
):
|
||||||
|
|
||||||
|
@ -581,7 +581,7 @@ async def test_ssdp_already_configured(
|
||||||
|
|
||||||
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
|
no_mac_address.return_value = "aa:bb:cc:dd:ee:ff"
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.device_info",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||||
return_value=MOCK_DEVICE_INFO,
|
return_value=MOCK_DEVICE_INFO,
|
||||||
):
|
):
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ async def test_setup_from_yaml_without_port_device_offline(hass: HomeAssistant)
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWS.open",
|
||||||
side_effect=OSError,
|
side_effect=OSError,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.device_info",
|
"homeassistant.components.samsungtv.bridge.SamsungTVWSBridge.async_device_info",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
):
|
):
|
||||||
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
|
await async_setup_component(hass, SAMSUNGTV_DOMAIN, MOCK_CONFIG)
|
||||||
|
|
Loading…
Reference in New Issue