From d7b7370c82b84027dcf2b77ef1bcde8b3fc676a3 Mon Sep 17 00:00:00 2001 From: Ong Vairoj Date: Sat, 9 Jun 2018 06:22:34 -0700 Subject: [PATCH] Samsung TV can't turn off after idle period (#14587) When Samsung TV is idle for a period of time after issued a command, subsequent 'turn_off' command won't turn off the TV. The issue is seen in Samsung models with websocket as discussed in #12302. == Reproducible Steps 1. Turn on TV (either via HA or directly). 2. Issue some commands e.g. volume ups / downs. 3. Wait for ~1 minute. 4. Issue turn_off command via HA. TV won't turn off. 5. Issue subsequent turn off commands won't turn off TV still. 6. However, issue some other commands e.g. volume ups / downs multiple times in a row and then turn_off will turn off the TV. == Root Cause The underlying websocket connection opened by samsungctl get closed after some idle time. There was no retry mechanism so issued commands would intermittently fail but the subsequent one would succeed when `_remote` get recreated. With `turn_off()`, however, there is an additional call to `self.get_remote().close()` which indirectly caused new connection to be created and then closed immediately. This causes the component to stuck in failure mode when turn_off command is repeatly issued. == The Fix Recreate the connection and retry the command if connection is closed to avoid silent failures due to connection closed. Also set `_remote` to None after calling close() to put it in correct state. This fix eliminates intermittent command failure and failure mode in turn_off(). --- .../components/media_player/samsungtv.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/media_player/samsungtv.py b/homeassistant/components/media_player/samsungtv.py index 0b7fc3c078e..43e9abd96a6 100644 --- a/homeassistant/components/media_player/samsungtv.py +++ b/homeassistant/components/media_player/samsungtv.py @@ -155,16 +155,25 @@ class SamsungTVDevice(MediaPlayerDevice): _LOGGER.info("TV is powering off, not sending command: %s", key) return try: - self.get_remote().control(key) + # recreate connection if connection was dead + retry_count = 1 + for _ in range(retry_count + 1): + try: + self.get_remote().control(key) + break + except (self._exceptions_class.ConnectionClosed, + BrokenPipeError): + # BrokenPipe can occur when the commands is sent to fast + self._remote = None self._state = STATE_ON except (self._exceptions_class.UnhandledResponse, - self._exceptions_class.AccessDenied, BrokenPipeError): + self._exceptions_class.AccessDenied): # We got a response so it's on. - # BrokenPipe can occur when the commands is sent to fast self._state = STATE_ON self._remote = None + _LOGGER.debug("Failed sending command %s", key, exc_info=True) return - except (self._exceptions_class.ConnectionClosed, OSError): + except OSError: self._state = STATE_OFF self._remote = None if self._power_off_in_progress(): @@ -207,6 +216,7 @@ class SamsungTVDevice(MediaPlayerDevice): # Force closing of remote session to provide instant UI feedback try: self.get_remote().close() + self._remote = None except OSError: _LOGGER.debug("Could not establish connection.")