Repository: postlund/pyatv
Pull request #2072 (2023-06-26 05:50:36)

helpers: Add is_device_supported
Add helper method is_device_supported, returning a boolean indicating if the device is supported by pyatv.

<a href="https://gitpod.io/#https://github.com/postlund/pyatv/pull/2072"><img src="https://gitpod.io/button/open-in-gitpod.svg"/></a>



codecov[bot]: ## [Codecov](https://app.codecov.io/gh/postlund/pyatv/pull/2072?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pierre+St%C3%A5hl) Report
Patch coverage: **`100.00`**% and project coverage change: **`-0.17`** :warning:
> Comparison is base [(`1630694`)](https://app.codecov.io/gh/postlund/pyatv/commit/16306949aa0b87b385202004a5e684c58ff84b4d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pierre+St%C3%A5hl) 88.78% compared to head [(`d09e6ab`)](https://app.codecov.io/gh/postlund/pyatv/pull/2072?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pierre+St%C3%A5hl) 88.62%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #2072      +/-   ##
==========================================
- Coverage   88.78%   88.62%   -0.17%     
==========================================
  Files         163      163              
  Lines       10841    10845       +4     
==========================================
- Hits         9625     9611      -14     
- Misses       1216     1234      +18     
```


| [Impacted Files](https://app.codecov.io/gh/postlund/pyatv/pull/2072?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pierre+St%C3%A5hl) | Coverage Δ | |
|---|---|---|
| [pyatv/helpers.py](https://app.codecov.io/gh/postlund/pyatv/pull/2072?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pierre+St%C3%A5hl#diff-cHlhdHYvaGVscGVycy5weQ==) | `100.00% <100.00%> (ø)` | |

... and [5 files with indirect coverage changes](https://app.codecov.io/gh/postlund/pyatv/pull/2072/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pierre+St%C3%A5hl)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/postlund/pyatv/pull/2072?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pierre+St%C3%A5hl).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pierre+St%C3%A5hl).


Patch:
diff --git a/docs/api/pyatv/helpers.html b/docs/api/pyatv/helpers.html
index cf897fc3..bf42f2c9 100644
--- a/docs/api/pyatv/helpers.html
+++ b/docs/api/pyatv/helpers.html
@@ -19,6 +19,7 @@ link_group: api
 <ul class="">
 <li><code><a title="pyatv.helpers.auto_connect" href="#pyatv.helpers.auto_connect">auto_connect</a></code></li>
 <li><code><a title="pyatv.helpers.get_unique_id" href="#pyatv.helpers.get_unique_id">get_unique_id</a></code></li>
+<li><code><a title="pyatv.helpers.is_device_supported" href="#pyatv.helpers.is_device_supported">is_device_supported</a></code></li>
 <li><code><a title="pyatv.helpers.is_streamable" href="#pyatv.helpers.is_streamable">is_streamable</a></code></li>
 </ul>
 </li>
@@ -30,7 +31,7 @@ link_group: api
 </header>
 <section id="section-intro">
 <p>Various helper methods.</p>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/helpers.py#L1-L102" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/helpers.py#L1-L122" class="git-link">Browse git</a></div>
 </section>
 <section>
 </section>
@@ -66,6 +67,19 @@ Very inflexible in many cases, but can be handys sometimes when trying things.</
 <p>The unique identifier is returned if available, otherwise <code>None</code> is returned.</p></section>
 <div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/helpers.py#L54-L87" class="git-link">Browse git</a></div>
 </dd>
+<dt id="pyatv.helpers.is_device_supported">
+<code class="name flex">
+<span>def <span class="ident">is_device_supported</span></span>(<span>conf: <a title="pyatv.interface.BaseConfig" href="interface#pyatv.interface.BaseConfig">BaseConfig</a>) -> bool</span>
+</code>
+</dt>
+<dd>
+<section class="desc"><p>Return if pyatv supports this device.</p>
+<p>This method will return False if all of its services are either
+PairingRequirement.Unsupported or PairingRequirement.Disabled. In all other cases
+it will return True. Do note that even if this method returns True, pairing (or
+that existing credentials are provided) might still be needed.</p></section>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/helpers.py#L105-L122" class="git-link">Browse git</a></div>
+</dd>
 <dt id="pyatv.helpers.is_streamable">
 <code class="name flex">
 <span>async def <span class="ident">is_streamable</span></span>(<span>filename: str) -> bool</span>
diff --git a/pyatv/helpers.py b/pyatv/helpers.py
index e851da69..23100c92 100644
--- a/pyatv/helpers.py
+++ b/pyatv/helpers.py
@@ -100,3 +100,23 @@ async def is_streamable(filename: str) -> bool:
     except Exception:
         return False
     return True
+
+
+def is_device_supported(conf: pyatv.interface.BaseConfig) -> bool:
+    """Return if pyatv supports this device.
+
+    This method will return False if all of its services are either
+    PairingRequirement.Unsupported or PairingRequirement.Disabled. In all other cases
+    it will return True. Do note that even if this method returns True, pairing (or
+    that existing credentials are provided) might still be needed.
+    """
+    # Gather a set of present pairing requirements, subtract unsupported requirements
+    # and check that we have something left.
+    dev_requirements = set(service.pairing for service in conf.services)
+    unsupported_requirements = set(
+        [
+            pyatv.const.PairingRequirement.Unsupported,
+            pyatv.const.PairingRequirement.Disabled,
+        ]
+    )
+    return len(dev_requirements.difference(unsupported_requirements)) > 0


Test patch:
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 6d9e2a13..7c3514cf 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -1,10 +1,11 @@
 """Functional tests for helper methods. Agnostic to protocol implementation."""
 
+from ipaddress import IPv4Address
 from unittest.mock import MagicMock, patch
 
 import pytest
 
-from pyatv import conf, helpers
+from pyatv import conf, const, helpers
 
 from tests.utils import data_path
 
@@ -101,3 +102,36 @@ def test_get_unique_id_(service_type, service_name, properties, expected_id):
 )
 async def test_is_streamable_supported_file(test_file, streamable):
     assert await helpers.is_streamable(data_path(test_file)) == streamable
+
+
+@pytest.mark.parametrize(
+    "pairing_requirement, is_supported",
+    [
+        (const.PairingRequirement.Unsupported, False),
+        (const.PairingRequirement.Disabled, False),
+        (const.PairingRequirement.NotNeeded, True),
+        (const.PairingRequirement.Optional, True),
+        (const.PairingRequirement.Mandatory, True),
+    ],
+)
+def test_is_device_supported(pairing_requirement, is_supported):
+    dev = conf.AppleTV(IPv4Address("127.0.0.1"), "test")
+    dev.add_service(
+        conf.ManualService(
+            "unsupported",
+            const.Protocol.DMAP,
+            0,
+            {},
+            pairing_requirement=const.PairingRequirement.Unsupported,
+        )
+    )
+    dev.add_service(
+        conf.ManualService(
+            "test",
+            const.Protocol.AirPlay,
+            0,
+            {},
+            pairing_requirement=pairing_requirement,
+        )
+    )
+    assert helpers.is_device_supported(dev) == is_supported