1 """Support for functionality to interact with Android / Fire TV devices."""
3 from __future__
import annotations
5 from datetime
import datetime, timedelta
9 from androidtv.constants
import APPS, KEYS
10 from androidtv.setup_async
import AndroidTVAsync, FireTVAsync
11 import voluptuous
as vol
15 MediaPlayerDeviceClass,
17 MediaPlayerEntityFeature,
27 from .
import AndroidTVConfigEntry
30 CONF_EXCLUDE_UNNAMED_APPS,
32 CONF_SCREENCAP_INTERVAL,
33 CONF_TURN_OFF_COMMAND,
35 DEFAULT_EXCLUDE_UNNAMED_APPS,
37 DEFAULT_SCREENCAP_INTERVAL,
41 from .entity
import AndroidTVEntity, adb_decorator
43 _LOGGER = logging.getLogger(__name__)
45 ATTR_ADB_RESPONSE =
"adb_response"
46 ATTR_DEVICE_PATH =
"device_path"
47 ATTR_HDMI_INPUT =
"hdmi_input"
48 ATTR_LOCAL_PATH =
"local_path"
50 SERVICE_ADB_COMMAND =
"adb_command"
51 SERVICE_DOWNLOAD =
"download"
52 SERVICE_LEARN_SENDEVENT =
"learn_sendevent"
53 SERVICE_UPLOAD =
"upload"
57 "off": MediaPlayerState.OFF,
58 "idle": MediaPlayerState.IDLE,
59 "standby": MediaPlayerState.STANDBY,
60 "playing": MediaPlayerState.PLAYING,
61 "paused": MediaPlayerState.PAUSED,
67 entry: AndroidTVConfigEntry,
68 async_add_entities: AddEntitiesCallback,
70 """Set up the Android Debug Bridge entity."""
71 device_class = entry.runtime_data.aftv.DEVICE_CLASS
75 if device_class == DEVICE_ANDROIDTV
80 platform = entity_platform.async_get_current_platform()
81 platform.async_register_entity_service(
83 {vol.Required(ATTR_COMMAND): cv.string},
86 platform.async_register_entity_service(
87 SERVICE_LEARN_SENDEVENT,
None,
"learn_sendevent"
89 platform.async_register_entity_service(
92 vol.Required(ATTR_DEVICE_PATH): cv.string,
93 vol.Required(ATTR_LOCAL_PATH): cv.string,
97 platform.async_register_entity_service(
100 vol.Required(ATTR_DEVICE_PATH): cv.string,
101 vol.Required(ATTR_LOCAL_PATH): cv.string,
108 """Representation of an Android or Fire TV device."""
110 _attr_device_class = MediaPlayerDeviceClass.TV
113 def __init__(self, entry: AndroidTVConfigEntry) ->
None:
114 """Initialize the Android / Fire TV device."""
118 self.
_media_image_media_image: tuple[bytes |
None, str |
None] =
None,
None
132 ATTR_ADB_RESPONSE:
None,
133 ATTR_HDMI_INPUT:
None,
140 """Load the config options."""
141 _LOGGER.debug(
"Loading configuration options")
144 apps = options.get(CONF_APPS, {})
148 value: key
for key, value
in self.
_app_id_to_name_app_id_to_name.items()
if value
153 for key, value
in apps.items():
156 self.
_get_sources_get_sources = options.get(CONF_GET_SOURCES, DEFAULT_GET_SOURCES)
158 CONF_EXCLUDE_UNNAMED_APPS, DEFAULT_EXCLUDE_UNNAMED_APPS
160 screencap_interval: int = options.get(
161 CONF_SCREENCAP_INTERVAL, DEFAULT_SCREENCAP_INTERVAL
163 if screencap_interval > 0:
171 """Set config parameter when add to hass."""
177 f
"{SIGNAL_CONFIG_ENTITY}_{self._entry_id}",
184 """Take a screen capture from the device."""
185 return await self.
aftvaftv.adb_screencap()
188 """Take a screen capture from the device when enabled."""
197 force: bool = prev_app_id
is not None
199 force = prev_app_id != self._attr_app_id
203 """Take a screen capture from the device every configured minutes."""
208 if not (force
or time_elapsed):
220 """Fetch current playing image."""
225 """Send play command."""
230 """Send pause command."""
235 """Send play/pause command."""
236 await self.
aftvaftv.media_play_pause()
240 """Turn on the device."""
248 """Turn off the device."""
256 """Send previous track command (results in rewind)."""
261 """Send next track command (results in fast-forward)."""
266 """Select input source.
268 If the source starts with a '!', then it will close the app instead of
271 if isinstance(source, str):
272 if not source.startswith(
"!"):
275 source_ = source[1:].lstrip()
280 """Send an ADB command to an Android / Fire TV device."""
281 if key := KEYS.get(command):
282 await self.
aftvaftv.adb_shell(f
"input keyevent {key}")
285 if command ==
"GET_PROPERTIES":
287 await self.
aftvaftv.get_properties_dict()
293 response = await self.
aftvaftv.adb_shell(command)
294 except UnicodeDecodeError:
297 if isinstance(response, str)
and response.strip():
305 """Translate a key press on a remote to ADB 'sendevent' commands."""
312 f
"Output from service '{SERVICE_LEARN_SENDEVENT}' from"
313 f
" {self.entity_id}: '{output}'"
315 persistent_notification.async_create(
318 title=
"Android Debug Bridge",
320 _LOGGER.debug(
"%s", msg)
324 """Download a file from your Android / Fire TV device to your Home Assistant instance."""
325 if not self.
hasshass.config.is_allowed_path(local_path):
326 _LOGGER.warning(
"'%s' is not secure to load data from!", local_path)
329 await self.
aftvaftv.adb_pull(local_path, device_path)
333 """Upload a file from your Home Assistant instance to an Android / Fire TV device."""
334 if not self.
hasshass.config.is_allowed_path(local_path):
335 _LOGGER.warning(
"'%s' is not secure to load data from!", local_path)
338 await self.
aftvaftv.adb_push(local_path, device_path)
342 """Representation of an Android device."""
344 _attr_supported_features = (
345 MediaPlayerEntityFeature.PAUSE
346 | MediaPlayerEntityFeature.PLAY
347 | MediaPlayerEntityFeature.TURN_ON
348 | MediaPlayerEntityFeature.TURN_OFF
349 | MediaPlayerEntityFeature.PREVIOUS_TRACK
350 | MediaPlayerEntityFeature.NEXT_TRACK
351 | MediaPlayerEntityFeature.SELECT_SOURCE
352 | MediaPlayerEntityFeature.STOP
353 | MediaPlayerEntityFeature.VOLUME_MUTE
354 | MediaPlayerEntityFeature.VOLUME_SET
355 | MediaPlayerEntityFeature.VOLUME_STEP
359 @adb_decorator(override_available=True)
361 """Update the device state and, if necessary, re-connect."""
375 prev_app_id = self._attr_app_id
382 self._attr_is_volume_muted,
391 if running_apps
and self._attr_app_id:
393 self._attr_app_id, self._attr_app_id
399 for app_id
in running_apps
409 """Send stop command."""
414 """Mute the volume."""
418 if is_muted
is not None and is_muted != mute:
423 """Set the volume level."""
428 """Send volume down command."""
433 """Send volume up command."""
438 """Representation of a Fire TV device."""
440 _attr_supported_features = (
441 MediaPlayerEntityFeature.PAUSE
442 | MediaPlayerEntityFeature.PLAY
443 | MediaPlayerEntityFeature.TURN_ON
444 | MediaPlayerEntityFeature.TURN_OFF
445 | MediaPlayerEntityFeature.PREVIOUS_TRACK
446 | MediaPlayerEntityFeature.NEXT_TRACK
447 | MediaPlayerEntityFeature.SELECT_SOURCE
448 | MediaPlayerEntityFeature.STOP
452 @adb_decorator(override_available=True)
454 """Update the device state and, if necessary, re-connect."""
468 prev_app_id = self._attr_app_id
481 if running_apps
and self._attr_app_id:
483 self._attr_app_id, self._attr_app_id
489 for app_id
in running_apps
499 """Send stop (back) command."""
500 await self.
aftvaftv.back()
None async_write_ha_state(self)
None async_on_remove(self, CALLBACK_TYPE func)
web.Response get(self, web.Request request, str config_key)
IssData update(pyiss.ISS iss)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)