diff --git a/example/art.py b/example/art.py index aa9f772..02eda82 100755 --- a/example/art.py +++ b/example/art.py @@ -12,6 +12,7 @@ def parseargs(): # Add command line argument parsing parser = argparse.ArgumentParser(description='Example art Samsung Frame TV.') parser.add_argument('ip', action="store", type=str, default=None, help='ip address of TV (default: %(default)s))') + parser.add_argument('-t','--token_file', action="store", type=str, default="token_file.txt", help='default token file to use (default: %(default)s))') parser.add_argument('-D','--debug', action='store_true', default=False, help='Debug mode (default: %(default)s))') return parser.parse_args() @@ -21,11 +22,11 @@ def main(): level=logging.DEBUG if args.debug else logging.INFO) logging.debug('debug mode') - # Example showing different token files for different tv's, with default of "token_file.txt" + # Example showing different token files for different tv's, with default of "token_file.txt" from args.token_file tokens = { '192.168.100.32' : "token_file1.txt", '192.168.100.73' : "token_file2.txt" } - token_file = tokens.get(args.ip, "token_file.txt") + token_file = tokens.get(args.ip, args.token_file) # Normal constructor (will ask for connection every time) #tv = SamsungTVWS(host=args.ip) diff --git a/example/async_art.py b/example/async_art.py index dd6f78e..8b98e1b 100755 --- a/example/async_art.py +++ b/example/async_art.py @@ -13,6 +13,7 @@ def parseargs(): # Add command line argument parsing parser = argparse.ArgumentParser(description='Example async art Samsung Frame TV.') parser.add_argument('ip', action="store", type=str, default=None, help='ip address of TV (default: %(default)s))') + parser.add_argument('-t','--token_file', action="store", type=str, default="token_file.txt", help='default token file to use (default: %(default)s))') parser.add_argument('-D','--debug', action='store_true', default=False, help='Debug mode (default: %(default)s))') return parser.parse_args() @@ -21,10 +22,11 @@ async def image_callback(event, response): async def main(): args = parseargs() - logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO) + logging.basicConfig(format='%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s', + level=logging.DEBUG if args.debug else logging.INFO) logging.debug('debug mode') # Autosave token to file - token_file = os.path.dirname(os.path.realpath(__file__)) + '/tv-token.txt' + token_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), args.token_file) tv = SamsungTVAsyncArt(host=args.ip, port=8002, token_file=token_file) await tv.start_listening() diff --git a/example/async_art_simple.py b/example/async_art_simple.py index 8b3eaed..d96eb75 100755 --- a/example/async_art_simple.py +++ b/example/async_art_simple.py @@ -13,6 +13,7 @@ def parseargs(): # Add command line argument parsing parser = argparse.ArgumentParser(description='Example async art Samsung Frame TV.') parser.add_argument('ip', action="store", type=str, default=None, help='ip address of TV (default: %(default)s))') + parser.add_argument('-t','--token_file', action="store", type=str, default="token_file.txt", help='default token file to use (default: %(default)s))') parser.add_argument('-D','--debug', action='store_true', default=False, help='Debug mode (default: %(default)s))') return parser.parse_args() @@ -23,7 +24,8 @@ async def main(): logging.debug('debug mode') logging.info('opening art websocket with token') - tv = SamsungTVAsyncArt(host=args.ip, port=8002, token_file="token_file.txt") + token_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), args.token_file) + tv = SamsungTVAsyncArt(host=args.ip, port=8002, token_file=token_file) await tv.start_listening() logging.info('getting tv info') diff --git a/example/async_art_slideshow_anything.py b/example/async_art_slideshow_anything.py index 9992401..dc926d9 100755 --- a/example/async_art_slideshow_anything.py +++ b/example/async_art_slideshow_anything.py @@ -41,6 +41,7 @@ def parseargs(): parser = argparse.ArgumentParser(description='Async Slideshow Any art on Samsung TV.') parser.add_argument('ip', action="store", type=str, default=None, help='ip address of TV (default: %(default)s))') parser.add_argument('-f','--folder', action="store", type=str, default="./slideshow", help='folder to store images in (default: %(default)s))') + parser.add_argument('-t','--token_file', action="store", type=str, default="token_file.txt", help='default token file to use (default: %(default)s))') parser.add_argument('-c','--check', action="store", type=int, default=60, help='how often to check for new art 0=run once (default: %(default)s))') parser.add_argument('-u','--update', action="store", type=float, default=2, help='random update period (mins) 0.25 minnimum (default: %(default)s))') parser.add_argument('-D','--debug', action='store_true', default=False, help='Debug mode (default: %(default)s))') @@ -64,18 +65,20 @@ class slideshow: type=artDataMixin, ) - def __init__(self, ip, folder, period=60, random_update=1440): + def __init__(self, ip, folder, period=60, random_update=1440, token_file=None): self.log = logging.getLogger('Main.'+__class__.__name__) self.debug = self.log.getEffectiveLevel() <= logging.DEBUG self.ip = ip self.random_update = max(0.25, random_update)*60 #convert minutes to seconds self.period = int(min(60, self.random_update)) if period else 0 + # Autosave token to file + self.token_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), token_file) if token_file else token_file self.category.SLIDESHOW.dir = folder self.category.MY_PHOTOS.dir = os.path.join(folder, self.category.MY_PHOTOS.dir_name) self.category.FAVOURITES.dir = os.path.join(folder, self.category.FAVOURITES.dir_name) self.api_version = 1 self.start = time.time() - self.tv = SamsungTVAsyncArt(host=self.ip, port=8002) + self.tv = SamsungTVAsyncArt(host=self.ip, port=8002, token_file=self.token_file) self.log.info('check thumbnails {}, slideshow rotation every: {}'.format('every {}s'.format(self.period) if self.period else 'once', datetime.timedelta(seconds = self.random_update))) try: @@ -234,10 +237,7 @@ async def select_artwork(self): start = True while True: try: - if not self.tv.is_alive(): - self.log.warning('reconnecting websocket') - await self.tv.start_listening() - if await self.tv.is_artmode(): + if await self.tv.in_artmode(): self.log.info('time to next rotation: {}'.format(self.get_countdown())) if not await self.do_random_update() and not start: await self.download_thmbnails() @@ -264,7 +264,8 @@ async def main(): mon = slideshow(args.ip, os.path.normpath(args.folder), period = args.check, - random_update = args.update) + random_update = args.update, + token_file = args.token_file) await mon.start_slideshow() diff --git a/samsungtvws/async_art.py b/samsungtvws/async_art.py index 8a89ff7..3a9cea7 100755 --- a/samsungtvws/async_art.py +++ b/samsungtvws/async_art.py @@ -96,19 +96,21 @@ async def open(self): raise exceptions.ConnectionFailure(response) return self.connection - + async def close(self): - if self.session and not self.session.closed: + if self.session: await self.session.close() await super().close() - + async def start_listening(self) -> None: # Override base class to process events - await super().start_listening(self.process_event) - try: - await self.get_artmode() - except AssertionError: - pass + if not self.is_alive(): + await self.open() + if await super().start_listening(self.process_event): + try: + await self.get_artmode() + except AssertionError: + pass def get_uuid(self): self.art_uuid = str(uuid.uuid4()) @@ -140,6 +142,7 @@ async def _send_art_request( request_data["id"] = self.get_uuid() #old api request_data["request_id"] = request_data["id"] #new api self.pending_requests[wait_for_event or request_data["id"]] = asyncio.Future() + await self.start_listening() await self.send_command(ArtChannelEmitCommand.art_app_request(request_data)) return await self.wait_for_response(wait_for_event or request_data["id"]) @@ -187,24 +190,22 @@ def _get_rest_api(self) -> SamsungTVAsyncRest: if self._rest_api is None: self._rest_api = SamsungTVAsyncRest(host=self.host, port=self.port, session=self.session) return self._rest_api - - async def supported(self) -> bool: + + async def _get_device_info(self): try: await asyncio.sleep(0.1) #do not hit rest api to frequently - data = await self._get_rest_api().rest_device_info() - return data.get("device", {}).get("FrameTVSupport") == "true" + return await self._get_rest_api().rest_device_info() except Exception as e: pass - return False + return {} + + async def supported(self) -> bool: + data = await self._get_device_info() + return data.get("device", {}).get("FrameTVSupport") == "true" async def on(self) -> bool: - try: - await asyncio.sleep(0.1) #do not hit rest api to frequently - data = await self._get_rest_api().rest_device_info() - return data.get("device", {}).get('PowerState', 'off') == 'on' - except Exception as e: - pass - return False + data = await self._get_device_info() + return data.get("device", {}).get('PowerState', 'off') == 'on' async def is_artmode(self) -> bool: return await self.on() and self.art_mode diff --git a/samsungtvws/async_connection.py b/samsungtvws/async_connection.py index 0aeaa02..be4c9e6 100644 --- a/samsungtvws/async_connection.py +++ b/samsungtvws/async_connection.py @@ -45,7 +45,7 @@ async def __aexit__( await self.close() async def open(self) -> WebSocketClientProtocol: - if self.connection: + if self.is_alive(): # someone else already created a new connection return self.connection @@ -83,14 +83,15 @@ async def start_listening( self, callback: Optional[Callable[[str, Any], Optional[Awaitable[None]]]] = None ) -> None: """Open, and start listening.""" - if self.connection: - raise exceptions.ConnectionFailure("Connection already exists") + if not self._recv_loop: + if not self.is_alive(): + self.connection = await self.open() - self.connection = await self.open() - - self._recv_loop = asyncio.ensure_future( - self._do_start_listening(callback, self.connection) - ) + self._recv_loop = asyncio.ensure_future( + self._do_start_listening(callback, self.connection) + ) + return True + return False async def _do_start_listening( self, @@ -98,6 +99,7 @@ async def _do_start_listening( connection: WebSocketClientProtocol, ) -> None: """Do start listening.""" + _LOGGING.debug("Listening Connection Started") with contextlib.suppress(ConnectionClosed): while True: data = await connection.recv() @@ -108,9 +110,11 @@ async def _do_start_listening( awaitable = callback(event, response) if awaitable: await awaitable + _LOGGING.debug("Listening Connection closed") + self._recv_loop = None async def close(self) -> None: - if self.connection: + if self.is_alive(): await self.connection.close() if self._recv_loop: await self._recv_loop @@ -123,7 +127,7 @@ async def send_commands( commands: Sequence[Union[SamsungTVCommand, Dict[str, Any]]], key_press_delay: Optional[float] = None, ) -> None: - if self.connection is None: + if not self.is_alive(): self.connection = await self.open() delay = self.key_press_delay if key_press_delay is None else key_press_delay