diff --git a/.coveragerc b/.coveragerc
index 02d59b55f5f2a1f35ebf0584557baf80cd32ed40..6b239402cb187fd2ce19b0250b38a2132efa85a8 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -31,7 +31,6 @@ omit =
     homeassistant/components/amcrest/*
     homeassistant/components/ampio/*
     homeassistant/components/android_ip_webcam/*
-    homeassistant/components/androidtv/*
     homeassistant/components/anel_pwrctrl/switch.py
     homeassistant/components/anthemav/media_player.py
     homeassistant/components/apache_kafka/*
diff --git a/homeassistant/components/androidtv/manifest.json b/homeassistant/components/androidtv/manifest.json
index 047eaaaf5db9c10f560b38fe4514fd1d6ab199b7..91ea4019c05f747ef8e24fc2d93632adeccdf29a 100644
--- a/homeassistant/components/androidtv/manifest.json
+++ b/homeassistant/components/androidtv/manifest.json
@@ -3,7 +3,7 @@
   "name": "Androidtv",
   "documentation": "https://www.home-assistant.io/components/androidtv",
   "requirements": [
-    "androidtv==0.0.24"
+    "androidtv==0.0.25"
   ],
   "dependencies": [],
   "codeowners": ["@JeffLIrion"]
diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py
index db4ff9e851ec8297e7991561651dbcdc96d2ff33..2db210b56f3e52ad74fa8c14b553087209cd0e8b 100644
--- a/homeassistant/components/androidtv/media_player.py
+++ b/homeassistant/components/androidtv/media_player.py
@@ -431,8 +431,10 @@ class AndroidTVDevice(ADBDevice):
             # Try to connect
             self._available = self.aftv.connect(always_log_errors=False)
 
-            # To be safe, wait until the next update to run ADB commands.
-            return
+            # To be safe, wait until the next update to run ADB commands if
+            # using the Python ADB implementation.
+            if not self.aftv.adb_server_ip:
+                return
 
         # If the ADB connection is not intact, don't update.
         if not self._available:
@@ -443,7 +445,9 @@ class AndroidTVDevice(ADBDevice):
             self.aftv.update()
         )
 
-        self._state = ANDROIDTV_STATES[state]
+        self._state = ANDROIDTV_STATES.get(state)
+        if self._state is None:
+            self._available = False
 
     @property
     def is_volume_muted(self):
@@ -506,8 +510,10 @@ class FireTVDevice(ADBDevice):
             # Try to connect
             self._available = self.aftv.connect(always_log_errors=False)
 
-            # To be safe, wait until the next update to run ADB commands.
-            return
+            # To be safe, wait until the next update to run ADB commands if
+            # using the Python ADB implementation.
+            if not self.aftv.adb_server_ip:
+                return
 
         # If the ADB connection is not intact, don't update.
         if not self._available:
@@ -518,7 +524,9 @@ class FireTVDevice(ADBDevice):
             self._get_sources
         )
 
-        self._state = ANDROIDTV_STATES[state]
+        self._state = ANDROIDTV_STATES.get(state)
+        if self._state is None:
+            self._available = False
 
     @property
     def source(self):
diff --git a/requirements_all.txt b/requirements_all.txt
index cb489a0aa681998670e084d321a7c13633676414..c241f5fd4a268b1fef97f79a865c79bda20d6ea4 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -194,7 +194,7 @@ ambiclimate==0.2.1
 amcrest==1.5.3
 
 # homeassistant.components.androidtv
-androidtv==0.0.24
+androidtv==0.0.25
 
 # homeassistant.components.anel_pwrctrl
 anel_pwrctrl-homeassistant==0.0.1.dev2
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index b1caf72deedd9c5737dd1522152bfeb3b011b0c8..ed0689654a691cf2523504ae9705a671db4662ef 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -78,6 +78,9 @@ aiowwlln==1.0.0
 # homeassistant.components.ambiclimate
 ambiclimate==0.2.1
 
+# homeassistant.components.androidtv
+androidtv==0.0.25
+
 # homeassistant.components.apns
 apns2==0.3.0
 
diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py
index ce0aa6721351f5bd4ba16095a0f38389a3f44ba3..6a181ab6b00a688e98171504b22a7dd65a039ae3 100755
--- a/script/gen_requirements_all.py
+++ b/script/gen_requirements_all.py
@@ -55,6 +55,7 @@ TEST_REQUIREMENTS = (
     "aiounifi",
     "aioswitcher",
     "aiowwlln",
+    "androidtv",
     "apns2",
     "aprslib",
     "av",
diff --git a/tests/components/androidtv/__init__.py b/tests/components/androidtv/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..34e8c745fdca6515270d6f8548c8d521b3291d3f
--- /dev/null
+++ b/tests/components/androidtv/__init__.py
@@ -0,0 +1 @@
+"""Tests for the androidtv component."""
diff --git a/tests/components/androidtv/test_media_player.py b/tests/components/androidtv/test_media_player.py
new file mode 100644
index 0000000000000000000000000000000000000000..e787fddd3bcb04e46ee0cef7ca899308d3a1eabf
--- /dev/null
+++ b/tests/components/androidtv/test_media_player.py
@@ -0,0 +1,232 @@
+"""The tests for the androidtv platform."""
+import logging
+from socket import error as socket_error
+import unittest
+from unittest.mock import patch
+
+from homeassistant.components.androidtv.media_player import (
+    AndroidTVDevice,
+    FireTVDevice,
+    setup,
+)
+
+
+def connect_device_success(self, *args, **kwargs):
+    """Return `self`, which will result in the ADB connection being interpreted as available."""
+    return self
+
+
+def connect_device_fail(self, *args, **kwargs):
+    """Raise a socket error."""
+    raise socket_error
+
+
+def adb_shell_python_adb_error(self, cmd):
+    """Raise an error that is among those caught for the Python ADB implementation."""
+    raise AttributeError
+
+
+def adb_shell_adb_server_error(self, cmd):
+    """Raise an error that is among those caught for the ADB server implementation."""
+    raise ConnectionResetError
+
+
+class AdbAvailable:
+    """A class that indicates the ADB connection is available."""
+
+    def shell(self, cmd):
+        """Send an ADB shell command (ADB server implementation)."""
+        return ""
+
+
+class AdbUnavailable:
+    """A class with ADB shell methods that raise errors."""
+
+    def __bool__(self):
+        """Return `False` to indicate that the ADB connection is unavailable."""
+        return False
+
+    def shell(self, cmd):
+        """Raise an error that pertains to the Python ADB implementation."""
+        raise ConnectionResetError
+
+
+PATCH_PYTHON_ADB_CONNECT_SUCCESS = patch(
+    "adb.adb_commands.AdbCommands.ConnectDevice", connect_device_success
+)
+PATCH_PYTHON_ADB_COMMAND_SUCCESS = patch(
+    "adb.adb_commands.AdbCommands.Shell", return_value=""
+)
+PATCH_PYTHON_ADB_CONNECT_FAIL = patch(
+    "adb.adb_commands.AdbCommands.ConnectDevice", connect_device_fail
+)
+PATCH_PYTHON_ADB_COMMAND_FAIL = patch(
+    "adb.adb_commands.AdbCommands.Shell", adb_shell_python_adb_error
+)
+PATCH_PYTHON_ADB_COMMAND_NONE = patch(
+    "adb.adb_commands.AdbCommands.Shell", return_value=None
+)
+
+PATCH_ADB_SERVER_CONNECT_SUCCESS = patch(
+    "adb_messenger.client.Client.device", return_value=AdbAvailable()
+)
+PATCH_ADB_SERVER_AVAILABLE = patch(
+    "androidtv.basetv.BaseTV.available", return_value=True
+)
+PATCH_ADB_SERVER_CONNECT_FAIL = patch(
+    "adb_messenger.client.Client.device", return_value=AdbUnavailable()
+)
+PATCH_ADB_SERVER_COMMAND_FAIL = patch(
+    "{}.AdbAvailable.shell".format(__name__), adb_shell_adb_server_error
+)
+PATCH_ADB_SERVER_COMMAND_NONE = patch(
+    "{}.AdbAvailable.shell".format(__name__), return_value=None
+)
+
+
+class TestAndroidTVPythonImplementation(unittest.TestCase):
+    """Test the androidtv media player for an Android TV device."""
+
+    def setUp(self):
+        """Set up an `AndroidTVDevice` media player."""
+        with PATCH_PYTHON_ADB_CONNECT_SUCCESS, PATCH_PYTHON_ADB_COMMAND_SUCCESS:
+            aftv = setup("IP:PORT", device_class="androidtv")
+            self.aftv = AndroidTVDevice(aftv, "Fake Android TV", {}, None, None)
+
+    def test_reconnect(self):
+        """Test that the error and reconnection attempts are logged correctly.
+
+        "Handles device/service unavailable. Log a warning once when
+        unavailable, log once when reconnected."
+
+        https://developers.home-assistant.io/docs/en/integration_quality_scale_index.html
+        """
+        with self.assertLogs(level=logging.WARNING) as logs:
+            with PATCH_PYTHON_ADB_CONNECT_FAIL, PATCH_PYTHON_ADB_COMMAND_FAIL:
+                for _ in range(5):
+                    self.aftv.update()
+                    self.assertFalse(self.aftv.available)
+                    self.assertIsNone(self.aftv.state)
+
+        assert len(logs.output) == 2
+        assert logs.output[0].startswith("ERROR")
+        assert logs.output[1].startswith("WARNING")
+
+        with self.assertLogs(level=logging.DEBUG) as logs:
+            with PATCH_PYTHON_ADB_CONNECT_SUCCESS, PATCH_PYTHON_ADB_COMMAND_SUCCESS:
+                # Update 1 will reconnect
+                self.aftv.update()
+                self.assertTrue(self.aftv.available)
+
+                # Update 2 will update the state
+                self.aftv.update()
+                self.assertTrue(self.aftv.available)
+                self.assertIsNotNone(self.aftv.state)
+
+        assert (
+            "ADB connection to {} successfully established".format(self.aftv.aftv.host)
+            in logs.output[0]
+        )
+
+    def test_adb_shell_returns_none(self):
+        """Test the case that the ADB shell command returns `None`.
+
+        The state should be `None` and the device should be unavailable.
+        """
+        with PATCH_PYTHON_ADB_COMMAND_NONE:
+            self.aftv.update()
+            self.assertFalse(self.aftv.available)
+            self.assertIsNone(self.aftv.state)
+
+        with PATCH_PYTHON_ADB_CONNECT_SUCCESS, PATCH_PYTHON_ADB_COMMAND_SUCCESS:
+            # Update 1 will reconnect
+            self.aftv.update()
+            self.assertTrue(self.aftv.available)
+
+            # Update 2 will update the state
+            self.aftv.update()
+            self.assertTrue(self.aftv.available)
+            self.assertIsNotNone(self.aftv.state)
+
+
+class TestAndroidTVServerImplementation(unittest.TestCase):
+    """Test the androidtv media player for an Android TV device."""
+
+    def setUp(self):
+        """Set up an `AndroidTVDevice` media player."""
+        with PATCH_ADB_SERVER_CONNECT_SUCCESS, PATCH_ADB_SERVER_AVAILABLE:
+            aftv = setup(
+                "IP:PORT", adb_server_ip="ADB_SERVER_IP", device_class="androidtv"
+            )
+            self.aftv = AndroidTVDevice(aftv, "Fake Android TV", {}, None, None)
+
+    def test_reconnect(self):
+        """Test that the error and reconnection attempts are logged correctly.
+
+        "Handles device/service unavailable. Log a warning once when
+        unavailable, log once when reconnected."
+
+        https://developers.home-assistant.io/docs/en/integration_quality_scale_index.html
+        """
+        with self.assertLogs(level=logging.WARNING) as logs:
+            with PATCH_ADB_SERVER_CONNECT_FAIL, PATCH_ADB_SERVER_COMMAND_FAIL:
+                for _ in range(5):
+                    self.aftv.update()
+                    self.assertFalse(self.aftv.available)
+                    self.assertIsNone(self.aftv.state)
+
+        assert len(logs.output) == 2
+        assert logs.output[0].startswith("ERROR")
+        assert logs.output[1].startswith("WARNING")
+
+        with self.assertLogs(level=logging.DEBUG) as logs:
+            with PATCH_ADB_SERVER_CONNECT_SUCCESS:
+                self.aftv.update()
+                self.assertTrue(self.aftv.available)
+                self.assertIsNotNone(self.aftv.state)
+
+        assert (
+            "ADB connection to {} via ADB server {}:{} successfully established".format(
+                self.aftv.aftv.host,
+                self.aftv.aftv.adb_server_ip,
+                self.aftv.aftv.adb_server_port,
+            )
+            in logs.output[0]
+        )
+
+    def test_adb_shell_returns_none(self):
+        """Test the case that the ADB shell command returns `None`.
+
+        The state should be `None` and the device should be unavailable.
+        """
+        with PATCH_ADB_SERVER_COMMAND_NONE:
+            self.aftv.update()
+            self.assertFalse(self.aftv.available)
+            self.assertIsNone(self.aftv.state)
+
+        with PATCH_ADB_SERVER_CONNECT_SUCCESS:
+            self.aftv.update()
+            self.assertTrue(self.aftv.available)
+            self.assertIsNotNone(self.aftv.state)
+
+
+class TestFireTVPythonImplementation(TestAndroidTVPythonImplementation):
+    """Test the androidtv media player for a Fire TV device."""
+
+    def setUp(self):
+        """Set up a `FireTVDevice` media player."""
+        with PATCH_PYTHON_ADB_CONNECT_SUCCESS, PATCH_PYTHON_ADB_COMMAND_SUCCESS:
+            aftv = setup("IP:PORT", device_class="firetv")
+            self.aftv = FireTVDevice(aftv, "Fake Fire TV", {}, True, None, None)
+
+
+class TestFireTVServerImplementation(TestAndroidTVServerImplementation):
+    """Test the androidtv media player for a Fire TV device."""
+
+    def setUp(self):
+        """Set up a `FireTVDevice` media player."""
+        with PATCH_ADB_SERVER_CONNECT_SUCCESS, PATCH_ADB_SERVER_AVAILABLE:
+            aftv = setup(
+                "IP:PORT", adb_server_ip="ADB_SERVER_IP", device_class="firetv"
+            )
+            self.aftv = FireTVDevice(aftv, "Fake Fire TV", {}, True, None, None)