Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Android TV media player integration #19157

Closed
wants to merge 19 commits into from
Closed

New Android TV media player integration #19157

wants to merge 19 commits into from

Conversation

a1ex4
Copy link

@a1ex4 a1ex4 commented Dec 10, 2018

Description:

This component allows to use any Android TV / Android device as a media player in Home-Assistant.

Right now there are two ways to communicate with an Android device, through the ADB protocol: use the Python implementation of the protocol, or connect to an external ADB server that is connected to the Android device. Given the many different combinations of Android devices and machines hosting HA, both methods are implemented in this component, trying to make the experience as streamlined as possible.

This PR is no longer WIP 🙂
Thanks in advance for any input!

Pull request in home-assistant.io with documentation (if applicable): home-assistant/home-assistant.io#7788

Example entry for configuration.yaml (if applicable):

media_player:
  # a device that does not require ADB authentication, or uses the generated ADB keys's default location
  - platform: androidtv
    host: 192.168.1.37
    name: MIBOX3
      
  # a device using moved ADB keys
  - platform: androidtv
    host: 192.168.1.37
    name: MIBOX3
    adbkey: /config/adbkey

  # using an external ADB server to connect to the device
  - platform: androidtv
    host: 192.168.1.37
    name: MIBOX3
    adb_server_ip: 127.0.0.1

  # custom user defined "known apps"
  - platform: androidtv
    host: 192.168.1.37
    name: MIBOX3
    apps:
      "amazon": "Amazon Premium Video"

Checklist:

  • The code change is tested and works locally.
  • Local tests pass with tox. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • New dependencies have been added to the REQUIREMENTS variable ([example][ex-requir]).
  • New dependencies are only imported inside functions that use them ([example][ex-import]).
  • New or updated dependencies have been added to requirements_all.txt by running script/gen_requirements_all.py.
  • New files were added to .coveragerc.

@a1ex4 a1ex4 changed the title WIP: New Android TV media player integration New Android TV media player integration Dec 11, 2018
Copy link
Member

@rytilahti rytilahti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some general comments about the code, no testing on a real device.



ACTIONS = {
"back": "4",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This listing should definitely go into the backend library.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to let it here because as it used for a service, there is a check to see if the requested action exists in the component. Also contributors can more easily add their actions in the component rather than in the backend lib.

}

KNOWN_APPS = {
"amazon": "Amazon Prime Video",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this too, or is there some reason for not doing so?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reasons, and also this is very specific to my component (the backend lib is made to work for the FireTV component too).


self._name = name
self._apps = KNOWN_APPS
self._apps.update(dict(apps))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understood correctly, this means that the KNOWN_APPS cannot be overridden nor hidden anyhow?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this just means that user can add their custom know apps in the configuration to be added to the KNOWN_APPS dict.

homeassistant/components/media_player/androidtv.py Outdated Show resolved Hide resolved
homeassistant/components/media_player/androidtv.py Outdated Show resolved Hide resolved
def media_play(self):
"""Send play command."""
self.androidtv.media_play()
self._state = STATE_PLAYING
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't recall right now if manual (assumed) state changes are preferred instead of waiting for the update() to update the state, so you may need to look it up.

JeffLIrion and others added 13 commits December 13, 2018 18:48
…roidTV.connect()' and 'AndroidTV.update()' methods

This commit requires an updated `androidtv` package, which is on GitHub but not yet on pypi.  If the code in this component and in the package look good, then I'll push the new version to pypi.
Don't access 'self.androidtv._available', get this info from the 'And…
Make 'self.exceptions' a tuple
Catch exceptions in 'adb_wrapper', not 'update'
Use 'atv.available' to avoid connecting twice
@arsaboo
Copy link
Contributor

arsaboo commented Jan 10, 2019

Tested on my Shield and got the following error:

Thu Jan 10 2019 15:03:48 GMT-0500 (Eastern Standard Time)

Error while setting up platform androidtv
Traceback (most recent call last):
  File "/srv/homeassistant/lib/python3.6/site-packages/homeassistant/helpers/entity_platform.py", line 128, in _async_setup_platform
    SLOW_SETUP_MAX_WAIT, loop=hass.loop)
  File "/usr/local/lib/python3.6/asyncio/tasks.py", line 358, in wait_for
    return fut.result()
  File "/usr/local/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/homeassistant/.homeassistant/custom_components/media_player/androidtv.py", line 181, in setup_platform
    atv = AndroidTV(host, adbkey)
  File "/srv/homeassistant/lib/python3.6/site-packages/androidtv/__init__.py", line 230, in __init__
    self.properties = self.device_info()
  File "/srv/homeassistant/lib/python3.6/site-packages/androidtv/__init__.py", line 327, in device_info
    btmac = re.findall(BTMAC_REGEX_PATTERN, properties)[0]
IndexError: list index out of range

@JeffLIrion
Copy link
Contributor

I just bumped the version of androidtv to 0.0.5 and pushed it to pypi.

@bobokun
Copy link

bobokun commented Jan 15, 2019

Also testing on my Shield after changing the requirements of androidtv==0.05 I'm getting an error:

Tue Jan 15 2019 15:55:28 GMT-0500 (Eastern Standard Time)

Error while setting up platform androidtv
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity_platform.py", line 128, in _async_setup_platform
    SLOW_SETUP_MAX_WAIT, loop=hass.loop)
  File "/usr/local/lib/python3.6/asyncio/tasks.py", line 358, in wait_for
    return fut.result()
  File "/usr/local/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/media_player/androidtv.py", line 181, in setup_platform
    atv = AndroidTV(host, adbkey)
  File "/config/deps/lib/python3.6/site-packages/androidtv/__init__.py", line 230, in __init__
    self.properties = self.device_info()
  File "/config/deps/lib/python3.6/site-packages/androidtv/__init__.py", line 330, in device_info
    wifi_out = self._adb_shell('ip addr show wlan0')
  File "/config/deps/lib/python3.6/site-packages/androidtv/__init__.py", line 474, in _adb_shell_python_adb
    return self._adb.Shell(cmd)
  File "/usr/local/lib/python3.6/site-packages/adb/adb_commands.py", line 376, in Shell
    timeout_ms=timeout_ms)
  File "/usr/local/lib/python3.6/site-packages/adb/adb_protocol.py", line 411, in Command
    return ''.join(cls.StreamingCommand(usb, service, command, timeout_ms))
  File "/usr/local/lib/python3.6/site-packages/adb/adb_protocol.py", line 439, in StreamingCommand
    for data in connection.ReadUntilClose():
AttributeError: 'NoneType' object has no attribute 'ReadUntilClose'

@JeffLIrion
Copy link
Contributor

@bobokun using the Python adb package can lead to those errors for many users / Android devices. You will likely have better luck running an ADB server instance and using that to communicate with your device. For hass.io, you can use the ADB server addon:

More info can be found in the forum thread:
https://community.home-assistant.io/t/native-support-for-android-tv-android-devices/82792

@JeffLIrion
Copy link
Contributor

Reviewers, what's needed to get this merged?

I submitted a couple PRs to @a1ex4 that bump the androidtv version to 0.0.5. I think the hold-ups are:

  • The ACTIONS dictionary
    • I'm not sure how best to handle this. We could instead import it from the androidtv package, but I think you're not supposed to import from packages in the main body of the code. But it needs to be in the main body since ACTIONS is used in SERVICE_ACTION_SCHEMA. The difficult thing is that in order to use that service, the user needs to know the valid actions.
  • The APPS dictionary
    • I think this could be moved to the package and imported in the AndroidTVDevice class. Maybe all stuff relating to app names should be moved into the package.
  • Setting self._state = .... This could be removed.

@bobokun
Copy link

bobokun commented Jan 31, 2019

@bobokun using the Python adb package can lead to those errors for many users / Android devices. You will likely have better luck running an ADB server instance and using that to communicate with your device. For hass.io, you can use the ADB server addon:

More info can be found in the forum thread:
https://community.home-assistant.io/t/native-support-for-android-tv-android-devices/82792

Thanks! Using the adb server addon made the component work on my ShieldTV

@arsaboo
Copy link
Contributor

arsaboo commented Feb 3, 2019

Yes, tried it with @frenck's addon and it worked with my Shield.

# "pure-python-adb"
atv = AndroidTV(
host,
adb_server_ip=config[CONF_ADB_SERVER_IP],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Question] Shouldn't you accept the adbkey for remote servers in the case someone is hosting ADB behind auth?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The adbkey configuration entry is only needed when using the Python implementation of the ADB protocol (aka, the adb package). When using an ADB server for ADB commands (such as the Hass.io addon), the adb instance is responsible for authenticating. Since that responsibility is outside the scope of this component, the adbkey configuration entry is not relevant.

@bieniu
Copy link
Member

bieniu commented Feb 14, 2019

Component doeasn't work with HA 0.88b0.

Error loading homeassistant.components.media_player.androidtv. Make sure all dependencies are installed
Traceback (most recent call last):
  File "/srv/homeassistant/lib/python3.6/site-packages/homeassistant/loader.py", line 147, in _load_file
    module = importlib.import_module(path)
  File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/srv/homeassistant/lib/python3.6/site-packages/homeassistant/components/media_player/androidtv.py", line 20, in <module>
    from homeassistant.components.media_player import (
ImportError: cannot import name 'SUPPORT_TURN_OFF'

@JeffLIrion
Copy link
Contributor

Component doeasn't work with HA 0.88b0.

Error loading homeassistant.components.media_player.androidtv. Make sure all dependencies are installed
Traceback (most recent call last):
  File "/srv/homeassistant/lib/python3.6/site-packages/homeassistant/loader.py", line 147, in _load_file
    module = importlib.import_module(path)
  File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/srv/homeassistant/lib/python3.6/site-packages/homeassistant/components/media_player/androidtv.py", line 20, in <module>
    from homeassistant.components.media_player import (
ImportError: cannot import name 'SUPPORT_TURN_OFF'

This pull request should fix the issue: https://github.com/a1ex4/home-assistant/pull/8

In the meantime, this component should work (and be backwards compatible): https://github.com/JeffLIrion/home-assistant/blob/androidtv-backwards-compatible/homeassistant/components/media_player/androidtv.py

@arsaboo
Copy link
Contributor

arsaboo commented Feb 14, 2019

@JeffLIrion Does it have to be placed in media_player/androidtv.py or androidtv/media_player?

@JeffLIrion
Copy link
Contributor

@arsaboo media_player/androidtv.py. If they're changing the directory structure for components in that way, I'm unaware of it.

@arsaboo
Copy link
Contributor

arsaboo commented Feb 14, 2019

@JeffLIrion Yes, we are moving to a different folder structure. From the release notes for 0.88

Note for custom component developers: We are moving to a new file structure. Platforms now live embedded in components. Custom platforms will have to be updated to follow this pattern. This is a breaking change in case your custom platform overrides a built-in platform. Rename your custom platform from, ie light/hue.py to hue/light.py.

If we use it under media_player/androidtv.py, I still get the error

Thu Feb 14 2019 11:57:46 GMT-0500 (Eastern Standard Time)
Error loading custom_components.androidtv.media_player. Make sure all dependencies are installed
Traceback (most recent call last):
  File "/srv/homeassistant/lib/python3.6/site-packages/homeassistant/loader.py", line 147, in _load_file
    module = importlib.import_module(path)
  File "/usr/local/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/homeassistant/.homeassistant/custom_components/androidtv/media_player.py", line 33, in <module>
    from homeassistant.components.media_player import (
ImportError: cannot import name 'SUPPORT_TURN_OFF'

and a warning about the folder change

Log Details (WARNING)
Thu Feb 14 2019 11:57:46 GMT-0500 (Eastern Standard Time)
Integrations need to be in their own folder. Change media_player/androidtv.py to androidtv/media_player.py. This will stop working soon.

@JeffLIrion
Copy link
Contributor

@arsaboo I think it will work if you use the backwards compatible version (and possibly rename it as you mentioned): https://github.com/JeffLIrion/home-assistant/blob/androidtv-backwards-compatible/homeassistant/components/media_player/androidtv.py

@tboyce021
Copy link
Contributor

Any chance of including hassio-addons/addon-adb#6 in this?

@jjlawren
Copy link
Contributor

jjlawren commented Mar 3, 2019

EDIT: It looks like it's just showing in the console, not actually hitting the logger.

EDIT 2: All the debug messages are from the adb_messenger include.

Looks like the androidtv import isn't handling logging properly, dumping every debug message back into HA even when the logger is set to 'warn' with:

logger:
    default: warn

Every few seconds messages like this are printed in my HA log:

03-03-2019:15:35:16,17 DEBUG    [connection.py:24] Connect to adb server - 127.0.0.1:5037
03-03-2019:15:35:16,20 DEBUG    [connection.py:75] b'000Chost:devices'
03-03-2019:15:35:16,22 DEBUG    [connection.py:47] Connection closed...
03-03-2019:15:35:16,23 DEBUG    [connection.py:24] Connect to adb server - 127.0.0.1:5037
03-03-2019:15:35:16,25 DEBUG    [connection.py:24] Connect to adb server - 127.0.0.1:5037
03-03-2019:15:35:16,27 DEBUG    [connection.py:75] b'0027host-serial:10.6.9.12:5555:get-serialno'
03-03-2019:15:35:16,28 DEBUG    [connection.py:47] Connection closed...
03-03-2019:15:35:16,29 DEBUG    [connection.py:24] Connect to adb server - 127.0.0.1:5037
03-03-2019:15:35:16,29 DEBUG    [connection.py:24] Connect to adb server - 127.0.0.1:5037
03-03-2019:15:35:16,30 DEBUG    [connection.py:75] b'001Dhost:transport:10.6.9.12:5555'
03-03-2019:15:35:16,30 DEBUG    [connection.py:75] b"0104shell:dumpsys power | grep 'Display Power' | grep -q 'state=ON' && echo -e '1\\c'  && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\\c'  && dumpsys power | grep Locks | grep 'size=' &&dumpsys window windows | grep mCurrentFocus && dumpsys audio"
03-03-2019:15:35:16,51 DEBUG    [connection.py:47] Connection closed...

Copy link
Member

@pvizeli pvizeli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small comments, after that we can merge it

name: MIBOX3
adbkey: /config/adbkey
apps:
"amazon": "Amazon Premium Video"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup this part

CONF_ADB_SERVER_IP = 'adb_server_ip'
CONF_ADB_SERVER_PORT = 'adb_server_port'

DEFAULT_APPS = {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove that

vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): vol.All(cv.port, cv.string),
vol.Optional(CONF_ADBKEY): has_adb_files,
vol.Optional(CONF_APPS, default=DEFAULT_APPS): dict,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default=dict()

@pvizeli pvizeli self-assigned this Mar 12, 2019
@JeffLIrion
Copy link
Contributor

@pvizeli I have a newer PR that combines the androidtv and firetv components (for ease of maintenance). Can we merge that PR instead.

#21944

@cgtobi
Copy link
Contributor

cgtobi commented Mar 15, 2019

Since #21944 has been merged we can close this, no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.