androidtvremote2 0.2.2__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {androidtvremote2-0.2.2/src/androidtvremote2.egg-info → androidtvremote2-0.2.3}/PKG-INFO +2 -2
  2. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/README.md +1 -1
  3. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/pyproject.toml +4 -5
  4. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/__init__.py +3 -0
  5. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/androidtv_remote.py +31 -18
  6. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/base.py +1 -1
  7. androidtvremote2-0.2.3/src/androidtvremote2/model.py +21 -0
  8. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/pairing.py +7 -4
  9. androidtvremote2-0.2.3/src/androidtvremote2/py.typed +0 -0
  10. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/remote.py +10 -9
  11. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3/src/androidtvremote2.egg-info}/PKG-INFO +2 -2
  12. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2.egg-info/SOURCES.txt +2 -0
  13. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/demo.py +30 -26
  14. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/LICENSE +0 -0
  15. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/setup.cfg +0 -0
  16. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/certificate_generator.py +0 -0
  17. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/const.py +0 -0
  18. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/exceptions.py +0 -0
  19. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/polo_pb2.py +0 -0
  20. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/polo_pb2.pyi +0 -0
  21. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/remotemessage_pb2.py +0 -0
  22. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2/remotemessage_pb2.pyi +0 -0
  23. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2.egg-info/dependency_links.txt +0 -0
  24. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2.egg-info/requires.txt +0 -0
  25. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/src/androidtvremote2.egg-info/top_level.txt +0 -0
  26. {androidtvremote2-0.2.2 → androidtvremote2-0.2.3}/tests/test_androidtv_remote.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: androidtvremote2
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A Python library for interacting with Android TV using the Android TV Remote protocol v2
5
5
  Author-email: tronikos <tronikos@gmail.com>
6
6
  License: Apache-2.0
@@ -19,7 +19,7 @@ Dynamic: license-file
19
19
  A Python library for interacting with Android TV using the Android TV Remote protocol v2. This is the same protocol the Google TV mobile app is using. It doesn't require ADB or enabling developer tools on the Android TV device. It only requires the [Android TV Remote Service](https://play.google.com/store/apps/details?id=com.google.android.tv.remote.service) that comes pre-installed on most Android TV devices.
20
20
 
21
21
  For a list of the most common commands you can send to the Android TV see: [TvKeys](https://github.com/tronikos/androidtvremote2/blob/main/TvKeys.txt).
22
- For a full list see [here](https://github.com/tronikos/androidtvremote2/blob/b4c49ac03043b1b9c40c2f2960e466d5a3b8bd67/src/androidtvremote2/remotemessage.proto#L90).
22
+ For a full list see [remotemessage.proto](https://github.com/tronikos/androidtvremote2/blob/b4c49ac03043b1b9c40c2f2960e466d5a3b8bd67/src/androidtvremote2/remotemessage.proto#L90).
23
23
  In addition to commands you can send URLs to open apps registered to handle them. See [this guide](https://community.home-assistant.io/t/android-tv-remote-app-links-deep-linking-guide/567921) for how to find deep links for apps.
24
24
 
25
25
  ## Credits
@@ -3,7 +3,7 @@
3
3
  A Python library for interacting with Android TV using the Android TV Remote protocol v2. This is the same protocol the Google TV mobile app is using. It doesn't require ADB or enabling developer tools on the Android TV device. It only requires the [Android TV Remote Service](https://play.google.com/store/apps/details?id=com.google.android.tv.remote.service) that comes pre-installed on most Android TV devices.
4
4
 
5
5
  For a list of the most common commands you can send to the Android TV see: [TvKeys](https://github.com/tronikos/androidtvremote2/blob/main/TvKeys.txt).
6
- For a full list see [here](https://github.com/tronikos/androidtvremote2/blob/b4c49ac03043b1b9c40c2f2960e466d5a3b8bd67/src/androidtvremote2/remotemessage.proto#L90).
6
+ For a full list see [remotemessage.proto](https://github.com/tronikos/androidtvremote2/blob/b4c49ac03043b1b9c40c2f2960e466d5a3b8bd67/src/androidtvremote2/remotemessage.proto#L90).
7
7
  In addition to commands you can send URLs to open apps registered to handle them. See [this guide](https://community.home-assistant.io/t/android-tv-remote-app-links-deep-linking-guide/567921) for how to find deep links for apps.
8
8
 
9
9
  ## Credits
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "androidtvremote2"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  license = {text = "Apache-2.0"}
5
5
  authors = [
6
6
  { name="tronikos", email="tronikos@gmail.com" },
@@ -32,12 +32,11 @@ combine_as_imports = true
32
32
  extend_skip_glob = ["*_pb2.py", "*_pb2_grpc.py"]
33
33
 
34
34
  [tool.ruff]
35
- target-version = "py311"
35
+ target-version = "py310"
36
36
  exclude = ["*_pb2.py", "*_pb2_grpc.py", "*.pyi"]
37
37
  line-length = 127
38
38
 
39
- [tool.ruff.lint]
40
- select = [
39
+ lint.select = [
41
40
  "B007", # Loop control variable {name} not used within loop body
42
41
  "B014", # Exception handler with duplicate exception
43
42
  "C", # complexity
@@ -61,7 +60,7 @@ select = [
61
60
  "W", # pycodestyle
62
61
  ]
63
62
 
64
- ignore = [
63
+ lint.ignore = [
65
64
  "D203", # 1 blank line required before class docstring
66
65
  "D213", # Multi-line docstring summary should start at the second line
67
66
  # keep-runtime-annotations
@@ -2,10 +2,13 @@
2
2
 
3
3
  from .androidtv_remote import AndroidTVRemote
4
4
  from .exceptions import CannotConnect, ConnectionClosed, InvalidAuth
5
+ from .model import DeviceInfo, VolumeInfo
5
6
 
6
7
  __all__ = [
7
8
  "AndroidTVRemote",
8
9
  "CannotConnect",
9
10
  "ConnectionClosed",
11
+ "DeviceInfo",
10
12
  "InvalidAuth",
13
+ "VolumeInfo",
11
14
  ]
@@ -14,6 +14,7 @@ from cryptography import x509
14
14
  from .certificate_generator import generate_selfsigned_cert
15
15
  from .const import LOGGER
16
16
  from .exceptions import CannotConnect, ConnectionClosed, InvalidAuth
17
+ from .model import DeviceInfo, VolumeInfo
17
18
  from .pairing import PairingProtocol
18
19
  from .remote import RemoteProtocol
19
20
  from .remotemessage_pb2 import RemoteDirection
@@ -64,12 +65,12 @@ class AndroidTVRemote:
64
65
  self._transport: asyncio.Transport | None = None
65
66
  self._remote_message_protocol: RemoteProtocol | None = None
66
67
  self._pairing_message_protocol: PairingProtocol | None = None
67
- self._reconnect_task: asyncio.Task | None = None
68
+ self._reconnect_task: asyncio.Task[None] | None = None
68
69
  self._ssl_context: ssl.SSLContext | None = None
69
- self._is_on_updated_callbacks: list[Callable] = []
70
- self._current_app_updated_callbacks: list[Callable] = []
71
- self._volume_info_updated_callbacks: list[Callable] = []
72
- self._is_available_updated_callbacks: list[Callable] = []
70
+ self._is_on_updated_callbacks: list[Callable[[bool], None]] = []
71
+ self._current_app_updated_callbacks: list[Callable[[str], None]] = []
72
+ self._volume_info_updated_callbacks: list[Callable[[VolumeInfo], None]] = []
73
+ self._is_available_updated_callbacks: list[Callable[[bool], None]] = []
73
74
 
74
75
  def is_on_updated(is_on: bool) -> None:
75
76
  for callback in self._is_on_updated_callbacks:
@@ -79,7 +80,7 @@ class AndroidTVRemote:
79
80
  for callback in self._current_app_updated_callbacks:
80
81
  callback(current_app)
81
82
 
82
- def volume_info_updated(volume_info: dict[str, str | bool]) -> None:
83
+ def volume_info_updated(volume_info: VolumeInfo) -> None:
83
84
  for callback in self._volume_info_updated_callbacks:
84
85
  callback(volume_info)
85
86
 
@@ -107,57 +108,67 @@ class AndroidTVRemote:
107
108
  return self._remote_message_protocol.current_app
108
109
 
109
110
  @property
110
- def device_info(self) -> dict[str, str] | None:
111
+ def device_info(self) -> DeviceInfo | None:
111
112
  """Device info (manufacturer, model, sw_version)."""
112
113
  if not self._remote_message_protocol:
113
114
  return None
114
115
  return self._remote_message_protocol.device_info
115
116
 
116
117
  @property
117
- def volume_info(self) -> dict[str, str | bool | int] | None:
118
+ def volume_info(self) -> VolumeInfo | None:
118
119
  """Volume info (level, max, muted)."""
119
120
  if not self._remote_message_protocol:
120
121
  return None
121
122
  return self._remote_message_protocol.volume_info
122
123
 
123
- def add_is_on_updated_callback(self, callback: Callable) -> None:
124
+ def add_is_on_updated_callback(self, callback: Callable[[bool], None]) -> None:
124
125
  """Add a callback for when is_on is updated."""
125
126
  self._is_on_updated_callbacks.append(callback)
126
127
 
127
- def remove_is_on_updated_callback(self, callback: Callable) -> None:
128
+ def remove_is_on_updated_callback(self, callback: Callable[[bool], None]) -> None:
128
129
  """Remove a callback previously added via add_is_on_updated_callback.
129
130
 
130
131
  :raises ValueError: if callback not previously added.
131
132
  """
132
133
  self._is_on_updated_callbacks.remove(callback)
133
134
 
134
- def add_current_app_updated_callback(self, callback: Callable) -> None:
135
+ def add_current_app_updated_callback(self, callback: Callable[[str], None]) -> None:
135
136
  """Add a callback for when current_app is updated."""
136
137
  self._current_app_updated_callbacks.append(callback)
137
138
 
138
- def remove_current_app_updated_callback(self, callback: Callable) -> None:
139
+ def remove_current_app_updated_callback(
140
+ self, callback: Callable[[str], None]
141
+ ) -> None:
139
142
  """Remove a callback previously added via add_current_app_updated_callback.
140
143
 
141
144
  :raises ValueError: if callback not previously added.
142
145
  """
143
146
  self._current_app_updated_callbacks.remove(callback)
144
147
 
145
- def add_volume_info_updated_callback(self, callback: Callable) -> None:
148
+ def add_volume_info_updated_callback(
149
+ self, callback: Callable[[VolumeInfo], None]
150
+ ) -> None:
146
151
  """Add a callback for when volume_info is updated."""
147
152
  self._volume_info_updated_callbacks.append(callback)
148
153
 
149
- def remove_volume_info_updated_callback(self, callback: Callable) -> None:
154
+ def remove_volume_info_updated_callback(
155
+ self, callback: Callable[[VolumeInfo], None]
156
+ ) -> None:
150
157
  """Remove a callback previously added via add_volume_info_updated_callback.
151
158
 
152
159
  :raises ValueError: if callback not previously added.
153
160
  """
154
161
  self._volume_info_updated_callbacks.remove(callback)
155
162
 
156
- def add_is_available_updated_callback(self, callback: Callable) -> None:
163
+ def add_is_available_updated_callback(
164
+ self, callback: Callable[[bool], None]
165
+ ) -> None:
157
166
  """Add a callback for when the Android TV is ready to receive commands or is unavailable."""
158
167
  self._is_available_updated_callbacks.append(callback)
159
168
 
160
- def remove_is_available_updated_callback(self, callback: Callable) -> None:
169
+ def remove_is_available_updated_callback(
170
+ self, callback: Callable[[bool], None]
171
+ ) -> None:
161
172
  """Remove a callback previously added via add_is_available_updated_callback.
162
173
 
163
174
  :raises ValueError: if callback not previously added.
@@ -245,7 +256,7 @@ class AndroidTVRemote:
245
256
  raise ConnectionClosed("Connection closed") from con_lost_exc
246
257
 
247
258
  async def _async_reconnect(
248
- self, invalid_auth_callback: Callable | None = None
259
+ self, invalid_auth_callback: Callable[[], None] | None = None
249
260
  ) -> None:
250
261
  while self._remote_message_protocol:
251
262
  exc = await self._remote_message_protocol.on_con_lost
@@ -279,7 +290,9 @@ class AndroidTVRemote:
279
290
  invalid_auth_callback()
280
291
  return
281
292
 
282
- def keep_reconnecting(self, invalid_auth_callback: Callable | None = None) -> None:
293
+ def keep_reconnecting(
294
+ self, invalid_auth_callback: Callable[[], None] | None = None
295
+ ) -> None:
283
296
  """Create a task to keep reconnecting whenever connection is lost."""
284
297
  self._reconnect_task = self._loop.create_task(
285
298
  self._async_reconnect(invalid_auth_callback)
@@ -16,7 +16,7 @@ from .const import LOGGER
16
16
  class ProtobufProtocol(asyncio.Protocol):
17
17
  """Protocol for receiving and sending protobuf messages."""
18
18
 
19
- def __init__(self, on_con_lost: asyncio.Future) -> None:
19
+ def __init__(self, on_con_lost: asyncio.Future[Exception | None]) -> None:
20
20
  """Initialize.
21
21
 
22
22
  :param on_con_lost: callback for when the connection is lost or closed.
@@ -0,0 +1,21 @@
1
+ """Data models for AndroidTVRemote."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TypedDict
6
+
7
+
8
+ class DeviceInfo(TypedDict):
9
+ """A TypedDict for device information."""
10
+
11
+ manufacturer: str
12
+ model: str
13
+ sw_version: str
14
+
15
+
16
+ class VolumeInfo(TypedDict):
17
+ """A TypedDict for volume information."""
18
+
19
+ level: int
20
+ max: int
21
+ muted: bool
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  import asyncio
11
11
  import hashlib
12
+ from typing import Any
12
13
 
13
14
  import aiofiles
14
15
  from cryptography import x509
@@ -45,7 +46,7 @@ class PairingProtocol(ProtobufProtocol):
45
46
 
46
47
  def __init__(
47
48
  self,
48
- on_con_lost: asyncio.Future,
49
+ on_con_lost: asyncio.Future[Exception | None],
49
50
  client_name: str,
50
51
  certfile: str,
51
52
  loop: asyncio.AbstractEventLoop,
@@ -62,8 +63,8 @@ class PairingProtocol(ProtobufProtocol):
62
63
  self._client_name = client_name
63
64
  self._certfile = certfile
64
65
  self._loop = loop
65
- self._on_pairing_started: asyncio.Future | None = None
66
- self._on_pairing_finished: asyncio.Future | None = None
66
+ self._on_pairing_started: asyncio.Future[bool] | None = None
67
+ self._on_pairing_finished: asyncio.Future[bool] | None = None
67
68
 
68
69
  async def async_start_pairing(self) -> None:
69
70
  """Start the pairing process.
@@ -130,7 +131,9 @@ class PairingProtocol(ProtobufProtocol):
130
131
  finally:
131
132
  self._on_pairing_finished = None
132
133
 
133
- async def _async_wait_for_future_or_con_lost(self, future: asyncio.Future) -> None:
134
+ async def _async_wait_for_future_or_con_lost(
135
+ self, future: asyncio.Future[Any]
136
+ ) -> None:
134
137
  """Wait for future to finish or connection to be lost."""
135
138
  await asyncio.wait(
136
139
  (self.on_con_lost, future), return_when=asyncio.FIRST_COMPLETED
File without changes
@@ -15,6 +15,7 @@ from google.protobuf.message import DecodeError
15
15
 
16
16
  from .base import ProtobufProtocol
17
17
  from .const import LOGGER
18
+ from .model import DeviceInfo, VolumeInfo
18
19
  from .remotemessage_pb2 import (
19
20
  RemoteDirection,
20
21
  RemoteEditInfo,
@@ -55,11 +56,11 @@ class RemoteProtocol(ProtobufProtocol):
55
56
 
56
57
  def __init__(
57
58
  self,
58
- on_con_lost: asyncio.Future,
59
- on_remote_started: asyncio.Future,
60
- on_is_on_updated: Callable,
61
- on_current_app_updated: Callable,
62
- on_volume_info_updated: Callable,
59
+ on_con_lost: asyncio.Future[Exception | None],
60
+ on_remote_started: asyncio.Future[bool],
61
+ on_is_on_updated: Callable[[bool], None],
62
+ on_current_app_updated: Callable[[str], None],
63
+ on_volume_info_updated: Callable[[VolumeInfo], None],
63
64
  loop: asyncio.AbstractEventLoop,
64
65
  enable_ime: bool,
65
66
  ) -> None:
@@ -89,12 +90,12 @@ class RemoteProtocol(ProtobufProtocol):
89
90
  )
90
91
  self.is_on = False
91
92
  self.current_app = ""
92
- self.device_info: dict[str, str] = {}
93
- self.volume_info: dict[str, str | bool | int] = {}
93
+ self.device_info: DeviceInfo | None = None
94
+ self.volume_info: VolumeInfo | None = None
94
95
  self.ime_counter: int = 0
95
96
  self.ime_field_counter: int = 0
96
97
  self._loop = loop
97
- self._idle_disconnect_task: asyncio.Task | None = None
98
+ self._idle_disconnect_task: asyncio.Task[None] | None = None
98
99
  self._reset_idle_disconnect_task()
99
100
 
100
101
  def send_key_command(
@@ -114,7 +115,7 @@ class RemoteProtocol(ProtobufProtocol):
114
115
  msg = RemoteMessage()
115
116
  if isinstance(key_code, str):
116
117
  if key_code.lower().startswith(TEXT_PREFIX):
117
- return self.send_text(key_code[len(TEXT_PREFIX):])
118
+ return self.send_text(key_code[len(TEXT_PREFIX) :])
118
119
  if not key_code.startswith(KEYCODE_PREFIX):
119
120
  key_code = KEYCODE_PREFIX + key_code
120
121
  key_code = RemoteKeyCode.Value(key_code)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: androidtvremote2
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A Python library for interacting with Android TV using the Android TV Remote protocol v2
5
5
  Author-email: tronikos <tronikos@gmail.com>
6
6
  License: Apache-2.0
@@ -19,7 +19,7 @@ Dynamic: license-file
19
19
  A Python library for interacting with Android TV using the Android TV Remote protocol v2. This is the same protocol the Google TV mobile app is using. It doesn't require ADB or enabling developer tools on the Android TV device. It only requires the [Android TV Remote Service](https://play.google.com/store/apps/details?id=com.google.android.tv.remote.service) that comes pre-installed on most Android TV devices.
20
20
 
21
21
  For a list of the most common commands you can send to the Android TV see: [TvKeys](https://github.com/tronikos/androidtvremote2/blob/main/TvKeys.txt).
22
- For a full list see [here](https://github.com/tronikos/androidtvremote2/blob/b4c49ac03043b1b9c40c2f2960e466d5a3b8bd67/src/androidtvremote2/remotemessage.proto#L90).
22
+ For a full list see [remotemessage.proto](https://github.com/tronikos/androidtvremote2/blob/b4c49ac03043b1b9c40c2f2960e466d5a3b8bd67/src/androidtvremote2/remotemessage.proto#L90).
23
23
  In addition to commands you can send URLs to open apps registered to handle them. See [this guide](https://community.home-assistant.io/t/android-tv-remote-app-links-deep-linking-guide/567921) for how to find deep links for apps.
24
24
 
25
25
  ## Credits
@@ -9,9 +9,11 @@ src/androidtvremote2/base.py
9
9
  src/androidtvremote2/certificate_generator.py
10
10
  src/androidtvremote2/const.py
11
11
  src/androidtvremote2/exceptions.py
12
+ src/androidtvremote2/model.py
12
13
  src/androidtvremote2/pairing.py
13
14
  src/androidtvremote2/polo_pb2.py
14
15
  src/androidtvremote2/polo_pb2.pyi
16
+ src/androidtvremote2/py.typed
15
17
  src/androidtvremote2/remote.py
16
18
  src/androidtvremote2/remotemessage_pb2.py
17
19
  src/androidtvremote2/remotemessage_pb2.pyi
@@ -14,6 +14,7 @@ from androidtvremote2 import (
14
14
  CannotConnect,
15
15
  ConnectionClosed,
16
16
  InvalidAuth,
17
+ VolumeInfo,
17
18
  )
18
19
 
19
20
  _LOGGER = logging.getLogger(__name__)
@@ -51,8 +52,8 @@ async def _bind_keyboard(remote: AndroidTVRemote) -> None:
51
52
  keyboard.Key.delete: "POWER",
52
53
  }
53
54
 
54
- def transmit_keys() -> asyncio.Queue:
55
- queue: asyncio.Queue = asyncio.Queue()
55
+ def transmit_keys() -> asyncio.Queue[keyboard.Key | keyboard.KeyCode | None]:
56
+ queue: asyncio.Queue[keyboard.Key | keyboard.KeyCode | None] = asyncio.Queue()
56
57
  loop = asyncio.get_event_loop()
57
58
 
58
59
  def on_press(key: keyboard.Key | keyboard.KeyCode | None) -> None:
@@ -64,30 +65,33 @@ async def _bind_keyboard(remote: AndroidTVRemote) -> None:
64
65
  key_queue = transmit_keys()
65
66
  while True:
66
67
  key = await key_queue.get()
68
+ if key is None:
69
+ continue
67
70
  if key in key_mappings:
68
71
  remote.send_key_command(key_mappings[key])
69
- if hasattr(key, "char"):
70
- if key.char == "q":
71
- remote.disconnect()
72
- return
73
- elif key.char == "m":
74
- remote.send_key_command("MUTE")
75
- elif key.char == "+":
76
- remote.send_key_command("VOLUME_UP")
77
- elif key.char == "-":
78
- remote.send_key_command("VOLUME_DOWN")
79
- elif key.char == "y":
80
- remote.send_launch_app_command("https://www.youtube.com")
81
- elif key.char == "n":
82
- remote.send_launch_app_command("com.netflix.ninja")
83
- elif key.char == "d":
84
- remote.send_launch_app_command("com.disney.disneyplus")
85
- elif key.char == "a":
86
- remote.send_launch_app_command("com.amazon.amazonvideo.livingroom")
87
- elif key.char == "k":
88
- remote.send_launch_app_command("org.xbmc.kodi")
89
- elif key.char == "t":
90
- remote.send_text("Hello World!")
72
+ if not isinstance(key, keyboard.KeyCode):
73
+ continue
74
+ if key.char == "q":
75
+ remote.disconnect()
76
+ return
77
+ elif key.char == "m":
78
+ remote.send_key_command("MUTE")
79
+ elif key.char == "+":
80
+ remote.send_key_command("VOLUME_UP")
81
+ elif key.char == "-":
82
+ remote.send_key_command("VOLUME_DOWN")
83
+ elif key.char == "y":
84
+ remote.send_launch_app_command("https://www.youtube.com")
85
+ elif key.char == "n":
86
+ remote.send_launch_app_command("com.netflix.ninja")
87
+ elif key.char == "d":
88
+ remote.send_launch_app_command("com.disney.disneyplus")
89
+ elif key.char == "a":
90
+ remote.send_launch_app_command("com.amazon.amazonvideo.livingroom")
91
+ elif key.char == "k":
92
+ remote.send_launch_app_command("org.xbmc.kodi")
93
+ elif key.char == "t":
94
+ remote.send_text("Hello World!")
91
95
 
92
96
 
93
97
  async def _host_from_zeroconf(timeout: float) -> str:
@@ -114,7 +118,7 @@ async def _host_from_zeroconf(timeout: float) -> str:
114
118
  for addr in info.parsed_scoped_addresses()
115
119
  ]
116
120
  print(f" Name: {name}")
117
- print(f" Addresses: {", ".join(addresses)}")
121
+ print(f" Addresses: {', '.join(addresses)}")
118
122
  if info.properties:
119
123
  print(" Properties:")
120
124
  for key, value in info.properties.items():
@@ -227,7 +231,7 @@ async def _main() -> None:
227
231
  def current_app_updated(current_app: str) -> None:
228
232
  _LOGGER.info("Notified that current_app: %s", current_app)
229
233
 
230
- def volume_info_updated(volume_info: dict[str, str | bool]) -> None:
234
+ def volume_info_updated(volume_info: VolumeInfo) -> None:
231
235
  _LOGGER.info("Notified that volume_info: %s", volume_info)
232
236
 
233
237
  def is_available_updated(is_available: bool) -> None: