Repository: postlund/pyatv
Pull request #1882 (2022-11-23 12:34:20)

Artwork_save file naming.
Added an extra parameter to the "artwork_save" function of the atvremote to be able to give the artwork file a specific name. If is not used, the file will still be called "artwork.png"

postlund: Looks good to me! But I would like that you add instructions to the documentation (https://pyatv.dev/documentation/atvremote/#play-status) about this, so people now how to use it.

Patch:
diff --git a/pyatv/protocols/companion/__init__.py b/pyatv/protocols/companion/__init__.py
index 7d48dda7..1dd28140 100644
--- a/pyatv/protocols/companion/__init__.py
+++ b/pyatv/protocols/companion/__init__.py
@@ -378,12 +378,29 @@ class CompanionRemoteControl(RemoteControl):
         await self._press_button(HidCommand.Screensaver)
 
     async def _press_button(
-        self, command: HidCommand, action: InputAction = InputAction.SingleTap
+        self,
+        command: HidCommand,
+        action: InputAction = InputAction.SingleTap,
+        delay: float = 1,
     ) -> None:
-        if action != InputAction.SingleTap:
-            raise NotImplementedError(f"{action} not supported for {command} (yet)")
-        await self.api.hid_command(True, command)
-        await self.api.hid_command(False, command)
+        if action == InputAction.SingleTap:
+            await self.api.hid_command(True, command)
+            await self.api.hid_command(False, command)
+
+        elif action == InputAction.Hold:
+            await self.api.hid_command(True, command)
+            await asyncio.sleep(delay)
+            await self.api.hid_command(False, command)
+
+        elif action == InputAction.DoubleTap:
+            # First press
+            await self.api.hid_command(True, command)
+            await self.api.hid_command(False, command)
+            # Second press
+            await self.api.hid_command(True, command)
+            await self.api.hid_command(False, command)
+        else:
+            raise exceptions.NotSupportedError(f"unsupported input action: {action}")
 
 
 class CompanionAudio(Audio):
diff --git a/pyatv/scripts/atvremote.py b/pyatv/scripts/atvremote.py
index 15f1a1b5..e6b55769 100644
--- a/pyatv/scripts/atvremote.py
+++ b/pyatv/scripts/atvremote.py
@@ -406,11 +406,11 @@ class DeviceCommands:
                 self.args, command, self.atv, self.storage, self.loop
             )
 
-    async def artwork_save(self, width=None, height=None):
+    async def artwork_save(self, width=None, height=None, file_name="artwork"):
         """Download artwork and save it to artwork.png."""
         artwork = await self.atv.metadata.artwork(width=width, height=height)
         if artwork is not None:
-            with open("artwork.png", "wb") as file:
+            with open(f"{file_name}.png", "wb") as file:
                 file.write(artwork.bytes)
         else:
             print("No artwork is currently available.")


Test patch:
diff --git a/tests/protocols/companion/test_companion_functional.py b/tests/protocols/companion/test_companion_functional.py
index a15d34f0..7b1a2763 100644
--- a/tests/protocols/companion/test_companion_functional.py
+++ b/tests/protocols/companion/test_companion_functional.py
@@ -218,6 +218,30 @@ async def test_remote_control_buttons(companion_client, companion_state, button)
     assert companion_state.latest_button == button
 
 
+@pytest.mark.parametrize(
+    "button,action",
+    [
+        ("up", InputAction.DoubleTap),
+        ("up", InputAction.Hold),
+        ("down", InputAction.DoubleTap),
+        ("down", InputAction.Hold),
+        ("left", InputAction.DoubleTap),
+        ("left", InputAction.Hold),
+        ("right", InputAction.DoubleTap),
+        ("right", InputAction.Hold),
+        ("select", InputAction.DoubleTap),
+        ("select", InputAction.Hold),
+        ("menu", InputAction.DoubleTap),
+        ("menu", InputAction.Hold),
+        ("home", InputAction.DoubleTap),
+        ("home", InputAction.Hold),
+    ],
+)
+async def test_button_actions(companion_client, companion_state, button, action):
+    await getattr(companion_client.remote_control, button)(action=action)
+    assert companion_state.latest_button == button
+
+
 async def test_remote_control_skip_forward_backward(companion_client, companion_state):
     duration = companion_state.duration
     await companion_client.remote_control.skip_forward()