diff --git a/androidtv/androidtv.py b/androidtv/androidtv.py index aedbc30f..87c3e495 100644 --- a/androidtv/androidtv.py +++ b/androidtv/androidtv.py @@ -74,7 +74,7 @@ def update(self): """ # Get the properties needed for the update - screen_on, awake, wake_lock_size, current_app, media_session_state, audio_state, device, is_volume_muted, volume = self.get_properties(lazy=True) + screen_on, awake, audio_state, wake_lock_size, current_app, media_session_state, device, is_volume_muted, volume = self.get_properties(lazy=True) # Get the volume (between 0 and 1) volume_level = self._volume_level(volume) @@ -192,14 +192,14 @@ def get_properties(self, lazy=False): Whether or not the device is on, or ``None`` if it was not determined awake : bool, None Whether or not the device is awake (screensaver is not running), or ``None`` if it was not determined + audio_state : str, None + The audio state, as determined from "dumpsys audio", or ``None`` if it was not determined wake_lock_size : int, None The size of the current wake lock, or ``None`` if it was not determined current_app : str, None The current app property, or ``None`` if it was not determined media_session_state : int, None The state from the output of ``dumpsys media_session``, or ``None`` if it was not determined - audio_state : str, None - The audio state, as determined from "dumpsys audio", or ``None`` if it was not determined device : str, None The current playback device, or ``None`` if it was not determined is_volume_muted : bool, None @@ -209,11 +209,12 @@ def get_properties(self, lazy=False): """ output = self.adb.shell(constants.CMD_SCREEN_ON + (constants.CMD_SUCCESS1 if lazy else constants.CMD_SUCCESS1_FAILURE0) + " && " + + constants.CMD_AUDIO_STATE + " && " + constants.CMD_AWAKE + (constants.CMD_SUCCESS1 if lazy else constants.CMD_SUCCESS1_FAILURE0) + " && " + constants.CMD_WAKE_LOCK_SIZE + " && " + constants.CMD_CURRENT_APP + " && (" + constants.CMD_MEDIA_SESSION_STATE + " || echo) && " + - "dumpsys audio") + constants.CMD_STREAM_MUSIC) _LOGGER.debug("Android TV %s update response: %s", self.host, output) # ADB command was unsuccessful @@ -222,43 +223,45 @@ def get_properties(self, lazy=False): # `screen_on` property if not output: - return False, False, -1, None, None, None, None, None, None + return False, False, None, -1, None, None, None, None, None screen_on = output[0] == '1' # `awake` property if len(output) < 2: - return screen_on, False, -1, None, None, None, None, None, None + return screen_on, False, None, -1, None, None, None, None, None awake = output[1] == '1' + # `audio_state` property + if len(output) < 3: + return screen_on, awake, None, -1, None, None, None, None, None + audio_state = self._audio_state(output[2]) + lines = output.strip().splitlines() # `wake_lock_size` property - if len(lines[0]) < 3: - return screen_on, awake, -1, None, None, None, None, None, None + if len(lines[0]) < 4: + return screen_on, awake, audio_state, -1, None, None, None, None, None wake_lock_size = self._wake_lock_size(lines[0]) # `current_app` property if len(lines) < 2: - return screen_on, awake, wake_lock_size, None, None, None, None, None, None + return screen_on, awake, audio_state, wake_lock_size, None, None, None, None, None current_app = self._current_app(lines[1]) # `media_session_state` property if len(lines) < 3: - return screen_on, awake, wake_lock_size, current_app, None, None, None, None, None + return screen_on, awake, audio_state, wake_lock_size, current_app, None, None, None, None media_session_state = self._media_session_state(lines[2], current_app) - # "dumpsys audio" output + # "STREAM_MUSIC" block if len(lines) < 4: - return screen_on, awake, wake_lock_size, current_app, media_session_state, None, None, None, None - - # reconstruct the output of `adb shell dumpsys audio` - dumpsys_audio = "\n".join(lines[3:]) + return screen_on, awake, audio_state, wake_lock_size, current_app, media_session_state, None, None, None - # `audio_state` property - audio_state = self._audio_state(dumpsys_audio) + # reconstruct the output of `constants.CMD_STREAM_MUSIC` + stream_music_raw = "\n".join(lines[3:]) # the "STREAM_MUSIC" block from `adb shell dumpsys audio` - stream_music = self._get_stream_music(dumpsys_audio) + stream_music = self._get_stream_music(stream_music_raw) # `device` property device = self._device(stream_music) @@ -269,7 +272,7 @@ def get_properties(self, lazy=False): # `is_volume_muted` property is_volume_muted = self._is_volume_muted(stream_music) - return screen_on, awake, wake_lock_size, current_app, media_session_state, audio_state, device, is_volume_muted, volume + return screen_on, awake, audio_state, wake_lock_size, current_app, media_session_state, device, is_volume_muted, volume def get_properties_dict(self, lazy=True): """Get the properties needed for Home Assistant updates and return them as a dictionary. @@ -286,14 +289,14 @@ def get_properties_dict(self, lazy=True): ``'media_session_state'``, ``'audio_state'``, ``'device'``, ``'is_volume_muted'``, and ``'volume'`` """ - screen_on, awake, wake_lock_size, current_app, media_session_state, audio_state, device, is_volume_muted, volume = self.get_properties(lazy=lazy) + screen_on, awake, audio_state, wake_lock_size, current_app, media_session_state, device, is_volume_muted, volume = self.get_properties(lazy=lazy) return {'screen_on': screen_on, 'awake': awake, + 'audio_state': audio_state, 'wake_lock_size': wake_lock_size, 'current_app': current_app, 'media_session_state': media_session_state, - 'audio_state': audio_state, 'device': device, 'is_volume_muted': is_volume_muted, 'volume': volume} diff --git a/androidtv/basetv.py b/androidtv/basetv.py index d81784a5..1462acb2 100644 --- a/androidtv/basetv.py +++ b/androidtv/basetv.py @@ -308,17 +308,11 @@ def audio_state(self): Returns ------- str, None - The audio state, as determined from the ADB shell command ``dumpsys audio``, or ``None`` if it could not be determined + The audio state, as determined from the ADB shell command :py:const:`androidtv.constants.CMD_AUDIO_STATE`, or ``None`` if it could not be determined """ audio_state_response = self.adb.shell(constants.CMD_AUDIO_STATE) - if audio_state_response is None: - return None - if audio_state_response == '1': - return constants.STATE_PAUSED - if audio_state_response == '2': - return constants.STATE_PLAYING - return constants.STATE_IDLE + return self._audio_state(audio_state_response) @property def available(self): @@ -477,13 +471,13 @@ def wake_lock_size(self): # # # ======================================================================= # @staticmethod - def _audio_state(dumpsys_audio_response): - """Parse the :attr:`audio_state` property from the output of ``adb shell dumpsys audio``. + def _audio_state(audio_state_response): + """Parse the :attr:`audio_state` property from the output of the command :py:const:`androidtv.constants.CMD_AUDIO_STATE`. Parameters ---------- - dumpsys_audio_response : str, None - The output of ``adb shell dumpsys audio`` + audio_state_response : str, None + The output of the command :py:const:`androidtv.constants.CMD_AUDIO_STATE` Returns ------- @@ -491,18 +485,12 @@ def _audio_state(dumpsys_audio_response): The audio state, or ``None`` if it could not be determined """ - if not dumpsys_audio_response: + if not audio_state_response: return None - - for line in dumpsys_audio_response.splitlines(): - if 'OpenSL ES AudioPlayer (Buffer Queue)' in line: - # ignore this line which can cause false positives for some apps (e.g. VRV) - continue - if 'started' in line: - return constants.STATE_PLAYING - if 'paused' in line: - return constants.STATE_PAUSED - + if audio_state_response == '1': + return constants.STATE_PAUSED + if audio_state_response == '2': + return constants.STATE_PLAYING return constants.STATE_IDLE @staticmethod @@ -579,27 +567,27 @@ def _device(stream_music): return None - def _get_stream_music(self, dumpsys_audio_response=None): - """Get the ``STREAM_MUSIC`` block from ``adb shell dumpsys audio``. + def _get_stream_music(self, stream_music_raw=None): + """Get the ``STREAM_MUSIC`` block from the output of the command :py:const:`androidtv.constants.CMD_STREAM_MUSIC`. Parameters ---------- - dumpsys_audio_response : str, None - The output of ``adb shell dumpsys audio`` + stream_music_raw : str, None + The output of the command :py:const:`androidtv.constants.CMD_STREAM_MUSIC` Returns ------- str, None - The ``STREAM_MUSIC`` block from ``adb shell dumpsys audio``, or ``None`` if it could not be determined + The ``STREAM_MUSIC`` block from the output of :py:const:`androidtv.constants.CMD_STREAM_MUSIC`, or ``None`` if it could not be determined """ - if not dumpsys_audio_response: - dumpsys_audio_response = self.adb.shell("dumpsys audio") + if not stream_music_raw: + stream_music_raw = self.adb.shell(constants.CMD_STREAM_MUSIC) - if not dumpsys_audio_response: + if not stream_music_raw: return None - matches = re.findall(constants.STREAM_MUSIC_REGEX_PATTERN, dumpsys_audio_response, re.DOTALL | re.MULTILINE) + matches = re.findall(constants.STREAM_MUSIC_REGEX_PATTERN, stream_music_raw, re.DOTALL | re.MULTILINE) if matches: return matches[0] @@ -747,7 +735,9 @@ def _wake_lock_size(wake_lock_size_response): """ if wake_lock_size_response: - return int(wake_lock_size_response.split("=")[1].strip()) + wake_lock_size_matches = constants.REGEX_WAKE_LOCK_SIZE.search(wake_lock_size_response) + if wake_lock_size_matches: + return int(wake_lock_size_matches.group('size')) return None diff --git a/androidtv/constants.py b/androidtv/constants.py index 107015c2..b1b4b869 100644 --- a/androidtv/constants.py +++ b/androidtv/constants.py @@ -38,6 +38,9 @@ #: Determine if the device is on CMD_SCREEN_ON = "dumpsys power | grep 'Display Power' | grep -q 'state=ON'" +#: Get the "STREAM MUSIC" block from ``dumpsys audio`` +CMD_STREAM_MUSIC = r"dumpsys audio | grep '\- STREAM_MUSIC:' -A 12" + #: Get the wake lock size CMD_WAKE_LOCK_SIZE = "dumpsys power | grep Locks | grep 'size='" @@ -259,6 +262,7 @@ # Regular expressions REGEX_MEDIA_SESSION_STATE = re.compile(r"state=(?P[0-9]+)", re.MULTILINE) +REGEX_WAKE_LOCK_SIZE = re.compile(r"size=(?P[0-9]+)") # Regular expression patterns DEVICE_REGEX_PATTERN = r"Devices: (.*?)\W" diff --git a/tests/test_androidtv.py b/tests/test_androidtv.py index d6e962ce..11f0e521 100644 --- a/tests/test_androidtv.py +++ b/tests/test_androidtv.py @@ -140,6 +140,21 @@ muted player piids:""" +# `dumpsys audio | grep '\- STREAM_MUSIC:' -A 12` +STREAM_MUSIC_OFF = """- STREAM_MUSIC: + Muted: false + Min: 0 + Max: 60 + Current: 2 (speaker): 20, 40000 (hmdi_arc): 27, 40000000 (default): 15 + Devices: speaker +- STREAM_ALARM: + Muted: true + Min: 0 + Max: 7 + Current: 2 (speaker): 3, 40000 (hmdi_arc): 3, 40000000 (default): 2 + Devices: speaker""" + + DUMPSYS_AUDIO_ON = """MediaFocusControl dump time: 9:03:06 AM Audio Focus stack entries (last is top of stack): @@ -262,14 +277,30 @@ muted player piids:""" + +# `dumpsys audio | grep '\- STREAM_MUSIC:' -A 12` +STREAM_MUSIC_ON = """- STREAM_MUSIC: + Muted: false + Min: 0 + Max: 60 + Current: 2 (speaker): 20, 40000 (hmdi_arc): 22, 40000000 (default): 15 + Devices: hmdi_arc +- STREAM_ALARM: + Muted: false + Min: 0 + Max: 7 + Current: 2 (speaker): 3, 40000 (hmdi_arc): 3, 40000000 (default): 2 + Devices: speaker""" + + # `dumpsys power | grep 'Display Power' | grep -q 'state=ON' && echo -e '1\c' && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\c' && dumpsys power | grep Locks | grep 'size=' && CURRENT_APP=$(dumpsys window windows | grep mCurrentFocus) && CURRENT_APP=${CURRENT_APP#*{* * } && CURRENT_APP=${CURRENT_APP%%/*} && echo $CURRENT_APP && (dumpsys media_session | grep -A 100 'Sessions Stack' | grep -A 100 $CURRENT_APP | grep -m 1 'state=PlaybackState {' || echo) && dumpsys audio` GET_PROPERTIES_OUTPUT1 = "" GET_PROPERTIES_DICT1 = {'screen_on': False, 'awake': False, + 'audio_state': None, 'wake_lock_size': -1, 'media_session_state': None, 'current_app': None, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} @@ -279,26 +310,26 @@ GET_PROPERTIES_OUTPUT2 = "1" GET_PROPERTIES_DICT2 = {'screen_on': True, 'awake': False, + 'audio_state': None, 'wake_lock_size': -1, 'media_session_state': None, 'current_app': None, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} STATE2 = (constants.STATE_IDLE, None, None, None, None) # `dumpsys power | grep 'Display Power' | grep -q 'state=ON' && echo -e '1\c' && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\c' && dumpsys power | grep Locks | grep 'size=' && CURRENT_APP=$(dumpsys window windows | grep mCurrentFocus) && CURRENT_APP=${CURRENT_APP#*{* * } && CURRENT_APP=${CURRENT_APP%%/*} && echo $CURRENT_APP && (dumpsys media_session | grep -A 100 'Sessions Stack' | grep -A 100 $CURRENT_APP | grep -m 1 'state=PlaybackState {' || echo) && dumpsys audio` -GET_PROPERTIES_OUTPUT3 = """11Wake Locks: size=2 +GET_PROPERTIES_OUTPUT3 = """110Wake Locks: size=2 com.amazon.tv.launcher -""" + DUMPSYS_AUDIO_ON +""" + STREAM_MUSIC_ON GET_PROPERTIES_DICT3 = {'screen_on': True, 'awake': True, + 'audio_state': constants.STATE_IDLE, 'wake_lock_size': 2, 'current_app': 'com.amazon.tv.launcher', 'media_session_state': None, - 'audio_state': constants.STATE_IDLE, 'device': 'hmdi_arc', 'is_volume_muted': False, 'volume': 22} @@ -306,77 +337,96 @@ GET_PROPERTIES_OUTPUT3A = GET_PROPERTIES_OUTPUT3[:1] GET_PROPERTIES_OUTPUT3B = GET_PROPERTIES_OUTPUT3[:2] -GET_PROPERTIES_OUTPUT3C = GET_PROPERTIES_OUTPUT3.splitlines()[0] -GET_PROPERTIES_OUTPUT3D = '\n'.join(GET_PROPERTIES_OUTPUT3.splitlines()[:2]) -GET_PROPERTIES_OUTPUT3E = '\n'.join(GET_PROPERTIES_OUTPUT3.splitlines()[:3]) -GET_PROPERTIES_OUTPUT3F = '\n'.join(GET_PROPERTIES_OUTPUT3.splitlines()[:4]) +GET_PROPERTIES_OUTPUT3C = GET_PROPERTIES_OUTPUT3[:3] +GET_PROPERTIES_OUTPUT3D = GET_PROPERTIES_OUTPUT3.splitlines()[0] +GET_PROPERTIES_OUTPUT3E = '\n'.join(GET_PROPERTIES_OUTPUT3.splitlines()[:2]) +GET_PROPERTIES_OUTPUT3F = '\n'.join(GET_PROPERTIES_OUTPUT3.splitlines()[:3]) +GET_PROPERTIES_OUTPUT3G = '\n'.join(GET_PROPERTIES_OUTPUT3.splitlines()[:4]) GET_PROPERTIES_DICT3A = {'screen_on': True, 'awake': False, + 'audio_state': None, 'wake_lock_size': -1, 'current_app': None, 'media_session_state': None, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} GET_PROPERTIES_DICT3B = {'screen_on': True, 'awake': True, + 'audio_state': None, 'wake_lock_size': -1, 'current_app': None, 'media_session_state': None, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} GET_PROPERTIES_DICT3C = {'screen_on': True, 'awake': True, - 'wake_lock_size': 2, + 'audio_state': constants.STATE_IDLE, + 'wake_lock_size': -1, 'current_app': None, 'media_session_state': None, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} GET_PROPERTIES_DICT3D = {'screen_on': True, 'awake': True, + 'audio_state': constants.STATE_IDLE, 'wake_lock_size': 2, - 'current_app': 'com.amazon.tv.launcher', + 'current_app': None, 'media_session_state': None, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} GET_PROPERTIES_DICT3E = {'screen_on': True, 'awake': True, + 'audio_state': constants.STATE_IDLE, + 'wake_lock_size': 2, + 'current_app': 'com.amazon.tv.launcher', + 'media_session_state': None, + 'device': None, + 'is_volume_muted': None, + 'volume': None} +GET_PROPERTIES_DICT3F = {'screen_on': True, + 'awake': True, + 'audio_state': constants.STATE_IDLE, + 'wake_lock_size': 2, + 'current_app': 'com.amazon.tv.launcher', + 'media_session_state': None, + 'device': None, + 'is_volume_muted': None, + 'volume': None} +GET_PROPERTIES_DICT3G = {'screen_on': True, + 'awake': True, + 'audio_state': constants.STATE_IDLE, 'wake_lock_size': 2, 'current_app': 'com.amazon.tv.launcher', 'media_session_state': None, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} -GET_PROPERTIES_OUTPUT4 = """11Wake Locks: size=2 +GET_PROPERTIES_OUTPUT4 = """111Wake Locks: size=2 com.amazon.tv.launcher -state=1 +state=PlaybackState {state=1, position=0, buffered position=0, speed=0.0, updated=65749, actions=240640, custom actions=[], active item id=-1, error=null} """ GET_PROPERTIES_DICT4 = {'screen_on': True, 'awake': True, + 'audio_state': constants.STATE_PAUSED, 'wake_lock_size': 2, 'current_app': 'com.amazon.tv.launcher', 'media_session_state': 1, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} GET_PROPERTIES_DICT_NONE = {'screen_on': None, 'awake': None, + 'audio_state': None, 'wake_lock_size': None, 'media_session_state': None, 'current_app': None, - 'audio_state': None, 'device': None, 'is_volume_muted': None, 'volume': None} @@ -390,17 +440,17 @@ 'standby']} # Plex: standby -GET_PROPERTIES_OUTPUT_PLEX_STANDBY = """11Wake Locks: size=1 +GET_PROPERTIES_OUTPUT_PLEX_STANDBY = """110Wake Locks: size=1 com.plexapp.android -""" + DUMPSYS_AUDIO_ON +""" + STREAM_MUSIC_ON GET_PROPERTIES_DICT_PLEX_STANDBY = {'screen_on': True, 'awake': True, + 'audio_state': constants.STATE_IDLE, 'wake_lock_size': 1, 'media_session_state': None, 'current_app': 'com.plexapp.android', - 'audio_state': constants.STATE_IDLE, 'device': 'hmdi_arc', 'is_volume_muted': False, 'volume': 22} @@ -408,17 +458,17 @@ STATE_PLEX_STANDBY = (constants.STATE_PLAYING, 'com.plexapp.android', 'hmdi_arc', False, 22/60.) # Plex: playing -GET_PROPERTIES_OUTPUT_PLEX_PLAYING = """11Wake Locks: size=3 +GET_PROPERTIES_OUTPUT_PLEX_PLAYING = """110Wake Locks: size=3 com.plexapp.android state=3 -""" + DUMPSYS_AUDIO_ON +""" + STREAM_MUSIC_ON GET_PROPERTIES_DICT_PLEX_PLAYING = {'screen_on': True, 'awake': True, + 'audio_state': constants.STATE_IDLE, 'wake_lock_size': 3, 'media_session_state': 3, 'current_app': 'com.plexapp.android', - 'audio_state': constants.STATE_IDLE, 'device': 'hmdi_arc', 'is_volume_muted': False, 'volume': 22} @@ -426,17 +476,17 @@ STATE_PLEX_PLAYING = (constants.STATE_PLAYING, 'com.plexapp.android', 'hmdi_arc', False, 22/60.) # Plex: paused -GET_PROPERTIES_OUTPUT_PLEX_PAUSED = """11Wake Locks: size=1 +GET_PROPERTIES_OUTPUT_PLEX_PAUSED = """110Wake Locks: size=1 com.plexapp.android state=3 -""" + DUMPSYS_AUDIO_ON +""" + STREAM_MUSIC_ON GET_PROPERTIES_DICT_PLEX_PAUSED = {'screen_on': True, 'awake': True, + 'audio_state': constants.STATE_IDLE, 'wake_lock_size': 1, 'media_session_state': 3, 'current_app': 'com.plexapp.android', - 'audio_state': constants.STATE_IDLE, 'device': 'hmdi_arc', 'is_volume_muted': False, 'volume': 22} @@ -470,7 +520,7 @@ def test_turn_on_off(self): self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, constants.CMD_SCREEN_ON + " && input keyevent {0}".format(constants.KEY_POWER)) def test_start_intent(self): - """Test that the ``AndroidTV.start_intent`` method works correctly. + """Test that the ``start_intent`` method works correctly. """ with patchers.patch_connect(True)[self.PATCH_KEY], patchers.patch_shell('')[self.PATCH_KEY]: @@ -489,25 +539,14 @@ def test_device(self): device = self.atv.device self.assertIsNone(device) - with patchers.patch_shell(DUMPSYS_AUDIO_OFF)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_OFF)[self.PATCH_KEY]: device = self.atv.device self.assertEqual('speaker', device) - with patchers.patch_shell(DUMPSYS_AUDIO_ON)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_ON)[self.PATCH_KEY]: device = self.atv.device self.assertEqual('hmdi_arc', device) - def test__audio_state(self): - """Check that the ``_audio_state`` method works correctly. - - """ - self.assertIsNone(self.atv._audio_state(None)) - self.assertIsNone(self.atv._audio_state('')) - self.assertEqual(self.atv._audio_state(DUMPSYS_AUDIO_OFF), constants.STATE_IDLE) - self.assertEqual(self.atv._audio_state(DUMPSYS_AUDIO_ON), constants.STATE_IDLE) - self.assertEqual(self.atv._audio_state('OpenSL ES AudioPlayer (Buffer Queue)\nstarted'), constants.STATE_PLAYING) - self.assertEqual(self.atv._audio_state('paused'), constants.STATE_PAUSED) - def test_volume(self): """Check that the ``volume`` property works correctly. @@ -520,12 +559,12 @@ def test_volume(self): volume = self.atv.volume self.assertIsNone(volume) - with patchers.patch_shell(DUMPSYS_AUDIO_OFF)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_OFF)[self.PATCH_KEY]: volume = self.atv.volume self.assertEqual(volume, 20) self.assertEqual(self.atv.max_volume, 60.) - with patchers.patch_shell(DUMPSYS_AUDIO_ON)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_ON)[self.PATCH_KEY]: volume = self.atv.volume self.assertEqual(volume, 22) self.assertEqual(self.atv.max_volume, 60.) @@ -542,12 +581,12 @@ def test_volume_level(self): volume_level = self.atv.volume_level self.assertIsNone(volume_level) - with patchers.patch_shell(DUMPSYS_AUDIO_OFF)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_OFF)[self.PATCH_KEY]: volume_level = self.atv.volume_level self.assertEqual(volume_level, 20./60) self.assertEqual(self.atv.max_volume, 60.) - with patchers.patch_shell(DUMPSYS_AUDIO_ON)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_ON)[self.PATCH_KEY]: volume_level = self.atv.volume_level self.assertEqual(volume_level, 22./60) self.assertEqual(self.atv.max_volume, 60.) @@ -564,7 +603,7 @@ def test_is_volume_muted(self): is_volume_muted = self.atv.is_volume_muted self.assertIsNone(is_volume_muted) - with patchers.patch_shell(DUMPSYS_AUDIO_OFF)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_OFF)[self.PATCH_KEY]: is_volume_muted = self.atv.is_volume_muted self.assertFalse(is_volume_muted) @@ -580,7 +619,7 @@ def test_set_volume_level(self): new_volume_level = self.atv.set_volume_level(0.5) self.assertIsNone(new_volume_level) - with patchers.patch_shell(DUMPSYS_AUDIO_ON)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_ON)[self.PATCH_KEY]: new_volume_level = self.atv.set_volume_level(0.5) self.assertEqual(new_volume_level, 0.5) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "(input keyevent 24 && sleep 1 && input keyevent 24 && sleep 1 && input keyevent 24 && sleep 1 && input keyevent 24 && sleep 1 && input keyevent 24 && sleep 1 && input keyevent 24 && sleep 1 && input keyevent 24 && sleep 1 && input keyevent 24) &") @@ -615,7 +654,7 @@ def test_volume_up(self): self.assertIsNone(new_volume_level) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 24") - with patchers.patch_shell(DUMPSYS_AUDIO_ON)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_ON)[self.PATCH_KEY]: new_volume_level = self.atv.volume_up() self.assertEqual(new_volume_level, 23./60) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 24") @@ -623,7 +662,7 @@ def test_volume_up(self): self.assertEqual(new_volume_level, 24./60) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 24") - with patchers.patch_shell(DUMPSYS_AUDIO_OFF)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_OFF)[self.PATCH_KEY]: new_volume_level = self.atv.volume_up() self.assertEqual(new_volume_level, 21./60) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 24") @@ -645,7 +684,7 @@ def test_volume_down(self): self.assertIsNone(new_volume_level) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 25") - with patchers.patch_shell(DUMPSYS_AUDIO_ON)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_ON)[self.PATCH_KEY]: new_volume_level = self.atv.volume_down() self.assertEqual(new_volume_level, 21./60) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 25") @@ -653,7 +692,7 @@ def test_volume_down(self): self.assertEqual(new_volume_level, 20./60) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 25") - with patchers.patch_shell(DUMPSYS_AUDIO_OFF)[self.PATCH_KEY]: + with patchers.patch_shell(STREAM_MUSIC_OFF)[self.PATCH_KEY]: new_volume_level = self.atv.volume_down() self.assertEqual(new_volume_level, 19./60) self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 25") @@ -662,7 +701,7 @@ def test_volume_down(self): self.assertEqual(getattr(self.atv.adb, self.ADB_ATTR).shell_cmd, "input keyevent 25") def test_get_properties(self): - """Check that ``get_properties()`` works correctly. + """Check that the ``get_properties`` method works correctly. """ with patchers.patch_shell(None)[self.PATCH_KEY]: @@ -701,6 +740,10 @@ def test_get_properties(self): properties = self.atv.get_properties_dict(lazy=True) self.assertEqual(properties, GET_PROPERTIES_DICT3E) + with patchers.patch_shell(GET_PROPERTIES_OUTPUT3F)[self.PATCH_KEY]: + properties = self.atv.get_properties_dict(lazy=True) + self.assertEqual(properties, GET_PROPERTIES_DICT3F) + with patchers.patch_shell(GET_PROPERTIES_OUTPUT4)[self.PATCH_KEY]: properties = self.atv.get_properties_dict(lazy=True) self.assertEqual(properties, GET_PROPERTIES_DICT4) @@ -781,99 +824,99 @@ def test_state_detection(self): """ self.atv.max_volume = 60. - self.assertUpdate([False, False, -1, None, None, None, None, None, None], + self.assertUpdate([False, False, None, -1, None, None, None, None, None], (constants.STATE_OFF, None, None, None, None)) - self.assertUpdate([True, False, -1, None, None, None, None, None, None], + self.assertUpdate([True, False, None, -1, None, None, None, None, None], (constants.STATE_IDLE, None, None, None, None)) # ATV Launcher - self.assertUpdate([True, True, 2, constants.APP_ATV_LAUNCHER, 3, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_ATV_LAUNCHER, 3, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, constants.APP_ATV_LAUNCHER, 'hmdi_arc', False, 0.5)) # ATV Launcher with custom state detection self.atv._state_detection_rules = {constants.APP_ATV_LAUNCHER: [{'idle': {'audio_state': 'idle'}}]} - self.assertUpdate([True, True, 2, constants.APP_ATV_LAUNCHER, 3, None, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_PAUSED, 2, constants.APP_ATV_LAUNCHER, 3, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, constants.APP_ATV_LAUNCHER, 'hmdi_arc', False, 0.5)) self.atv._state_detection_rules = {constants.APP_ATV_LAUNCHER: [{'idle': {'INVALID': 'idle'}}]} - self.assertUpdate([True, True, 2, constants.APP_ATV_LAUNCHER, 3, None, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_ATV_LAUNCHER, 3, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, constants.APP_ATV_LAUNCHER, 'hmdi_arc', False, 0.5)) self.atv._state_detection_rules = None # Bell Fibe - self.assertUpdate([True, True, 2, constants.APP_BELL_FIBE, 3, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_BELL_FIBE, 3, 'hmdi_arc', False, 30], (constants.STATE_IDLE, constants.APP_BELL_FIBE, 'hmdi_arc', False, 0.5)) # Netflix - self.assertUpdate([True, True, 2, constants.APP_NETFLIX, 2, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_NETFLIX, 2, 'hmdi_arc', False, 30], (constants.STATE_PAUSED, constants.APP_NETFLIX, 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 2, constants.APP_NETFLIX, 3, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_NETFLIX, 3, 'hmdi_arc', False, 30], (constants.STATE_PLAYING, constants.APP_NETFLIX, 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 2, constants.APP_NETFLIX, 4, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_NETFLIX, 4, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, constants.APP_NETFLIX, 'hmdi_arc', False, 0.5)) # Plex - self.assertUpdate([True, True, 2, constants.APP_PLEX, 4, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_PLEX, 4, 'hmdi_arc', False, 30], (constants.STATE_IDLE, constants.APP_PLEX, 'hmdi_arc', False, 0.5)) # TVheadend - self.assertUpdate([True, True, 5, constants.APP_TVHEADEND, 4, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 5, constants.APP_TVHEADEND, 4, 'hmdi_arc', False, 30], (constants.STATE_PAUSED, constants.APP_TVHEADEND, 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 6, constants.APP_TVHEADEND, 4, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 6, constants.APP_TVHEADEND, 4, 'hmdi_arc', False, 30], (constants.STATE_PLAYING, constants.APP_TVHEADEND, 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 1, constants.APP_TVHEADEND, 4, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 1, constants.APP_TVHEADEND, 4, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, constants.APP_TVHEADEND, 'hmdi_arc', False, 0.5)) # VLC - self.assertUpdate([True, True, 6, constants.APP_VLC, 2, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 6, constants.APP_VLC, 2, 'hmdi_arc', False, 30], (constants.STATE_PAUSED, constants.APP_VLC, 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 6, constants.APP_VLC, 3, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 6, constants.APP_VLC, 3, 'hmdi_arc', False, 30], (constants.STATE_PLAYING, constants.APP_VLC, 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 6, constants.APP_VLC, 4, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 6, constants.APP_VLC, 4, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, constants.APP_VLC, 'hmdi_arc', False, 0.5)) # VRV - self.assertUpdate([True, True, 2, constants.APP_VRV, 3, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_VRV, 3, 'hmdi_arc', False, 30], (constants.STATE_IDLE, constants.APP_VRV, 'hmdi_arc', False, 0.5)) # YouTube - self.assertUpdate([True, True, 2, constants.APP_YOUTUBE, 2, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_YOUTUBE, 2, 'hmdi_arc', False, 30], (constants.STATE_PAUSED, constants.APP_YOUTUBE, 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 2, constants.APP_YOUTUBE, 3, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_YOUTUBE, 3, 'hmdi_arc', False, 30], (constants.STATE_PLAYING, constants.APP_YOUTUBE, 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 2, constants.APP_YOUTUBE, 4, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, constants.APP_YOUTUBE, 4, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, constants.APP_YOUTUBE, 'hmdi_arc', False, 0.5)) # Unknown app - self.assertUpdate([True, True, 2, 'unknown', 2, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, 'unknown', 2, 'hmdi_arc', False, 30], (constants.STATE_PAUSED, 'unknown', 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 2, 'unknown', 3, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, 'unknown', 3, 'hmdi_arc', False, 30], (constants.STATE_PLAYING, 'unknown', 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 2, 'unknown', 4, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, 'unknown', 4, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, 'unknown', 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 2, 'unknown', None, constants.STATE_PLAYING, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_PLAYING, 2, 'unknown', None, 'hmdi_arc', False, 30], (constants.STATE_PLAYING, 'unknown', 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 1, 'unknown', None, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 1, 'unknown', None, 'hmdi_arc', False, 30], (constants.STATE_PAUSED, 'unknown', 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 2, 'unknown', None, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 2, 'unknown', None, 'hmdi_arc', False, 30], (constants.STATE_PLAYING, 'unknown', 'hmdi_arc', False, 0.5)) - self.assertUpdate([True, True, 3, 'unknown', None, constants.STATE_IDLE, 'hmdi_arc', False, 30], + self.assertUpdate([True, True, constants.STATE_IDLE, 3, 'unknown', None, 'hmdi_arc', False, 30], (constants.STATE_STANDBY, 'unknown', 'hmdi_arc', False, 0.5)) diff --git a/tests/test_basetv.py b/tests/test_basetv.py index a21af737..3dee55a9 100644 --- a/tests/test_basetv.py +++ b/tests/test_basetv.py @@ -411,14 +411,17 @@ def test_wake_lock_size(self): """ with patchers.patch_shell(None)[self.PATCH_KEY]: - self.assertEqual(self.btv.wake_lock_size, None) + self.assertIsNone(self.btv.wake_lock_size) with patchers.patch_shell('')[self.PATCH_KEY]: - self.assertEqual(self.btv.wake_lock_size, None) + self.assertIsNone(self.btv.wake_lock_size) with patchers.patch_shell('Wake Locks: size=2')[self.PATCH_KEY]: self.assertEqual(self.btv.wake_lock_size, 2) + with patchers.patch_shell('INVALID')[self.PATCH_KEY]: + self.assertIsNone(self.btv.wake_lock_size) + class TestHAStateDetectionRulesValidator(unittest.TestCase): def test_ha_state_detection_rules_validator(self): diff --git a/tests/test_firetv.py b/tests/test_firetv.py index 9dbc5551..952f23f0 100644 --- a/tests/test_firetv.py +++ b/tests/test_firetv.py @@ -94,7 +94,7 @@ GET_PROPERTIES_OUTPUT4 = """11Wake Locks: size=2 com.amazon.tv.launcher -state=2""" +state=PlaybackState {state=2, position=0, buffered position=0, speed=0.0, updated=65749, actions=240640, custom actions=[], active item id=-1, error=null}""" GET_PROPERTIES_DICT4 = {'screen_on': True, 'awake': True, 'wake_lock_size': 2, @@ -104,7 +104,7 @@ GET_PROPERTIES_OUTPUT5 = """11Wake Locks: size=2 com.amazon.tv.launcher -state=2 +state=PlaybackState {state=2, position=0, buffered position=0, speed=0.0, updated=65749, actions=240640, custom actions=[], active item id=-1, error=null} u0_a2 17243 197 998628 24932 ffffffff 00000000 S com.amazon.device.controllermanager u0_a2 17374 197 995368 20764 ffffffff 00000000 S com.amazon.device.controllermanager:BluetoothReceiver""" GET_PROPERTIES_DICT5 = {'screen_on': True,