[Testers needed!] Custom state detection rules for Android TV / Fire TV

Correct state detection is a known issue for the Android TV integration. The way it works is by collecting some properties and then using some simple logic to determine the state. The challenge is that there’s no one way to determine the state that works for all apps, so logic needs to be implemented for each app in order to ensure correct results. This logic might even need to differ from one Android TV device to the next.

A solution that was suggested in a GitHub issue was to allow the user to provide custom rules for determining the state. I implemented that, but I haven’t had a chance to test it yet. That’s where you come in!

Here is a custom component for testing: https://github.com/JeffLIrion/ha-androidtv/tree/custom_state_detection/custom_components/androidtv

You need all the files in that directory. And make sure that you get the files from the custom_state_detection branch!

Example config

media_player:
- platform: androidtv
  name: Fire TV (Bedroom)
  device_class: firetv
  host: 192.168.0.10
  state_detection_rules:
    'com.amazon.tv.launcher':
      - 'standby'
    'com.netflix.ninja':
      - 'media_session_state'
    'com.hulu.plus':
      - 'media_session_state'
      - 'wake_lock_size':
          1: 'playing'  # this indentation is important!
          2: 'paused'   # this indentation is important!
      - 'audio_state'

Follow-up post (so that the first post isn’t too lengthy)

For the state_detection_rules config parameter, the keys are app IDs and the values are lists of rules that are evaluated in order. Valid rules are:

  • 'standby', 'playing', 'paused', 'idle', or 'off'= always use the specified state for the state when this app is open
  • 'media_session_state' = try to use the media_session_state property to determine the state
  • 'audio_state' = try to use the audio_state property to determine the state
  • {'wake_lock_size': {VAL1: STATE1, VAL2: STATE:2, ...}} = try to look up the state using the wake_lock_size property

I’m not sure if this is working as intended or not. The latest update for the Shield reports audio_state as playing at all times if you enable the comfort noise feature designed to keep receivers from going to sleep. Probably technically correct since there is audio playing, just breaks that method of detection for us which is unfortunate.

Because of that change, I was trying to use these rules to only grab media_session_state for all applications but it doesn’t seem to be working. I’ve tried with Plex and a few others and they still seem to use audio_state instead of media_session_state.

Config:

- platform: androidtv
  host: xxx.xxx.xxx.xxx
  name: ADB Den Shield TV
  adb_server_ip: xxx.xxx.xxx.xx
  adb_server_port: xxxx
  get_sources: false
  device_class: androidtv
  apps:
    air.com.vudu.air.DownloaderTablet: 'Vudu'
    com.amazon.amazonvideo.livingroom: 'Amazon Prime'
    com.hbo.hbonow: 'HBO Now'
    com.hulu.livingroomplus: 'Hulu'
    com.google.android.tv: 'Live Channels'
    com.google.android.youtube.tv: 'YouTube'
    com.android.tv.settings: 'Settings'
    com.pbs.video: 'PBS'
    com.plexapp.android: 'Plex'
    org.xbmc.kodi: 'Kodi'
    tv.emby.embyatv: 'Emby'
  state_detection_rules:
    'com.android.vending':
      - 'standby'
    'com.android.tv.settings':
      - 'standby'
    'com.amazon.tv.launcher':
      - 'standby'
    'com.netflix.ninja':
      - 'media_session_state'
    'com.plexapp.android':
      - 'media_session_state'
    'com.amazon.amazonvideo.livingroom':
      - 'media_session_state'
    'com.hbo.hbonow':
      - 'media_session_state'
    'com.hulu.livingroomplus':
      - 'media_session_state'
      - 'wake_lock_size':
          1: 'playing'  # this indentation is important!
          2: 'paused'   # this indentation is important!

I am gonna test it to tomorrow!

It’s probably not getting anything for the media_session_state property. Try putting something like - 'idle' after it to verify that it’s working.

Yup, that seems to be the case. As you can see from the logs, media_session_state only gets set if a video is playing, but doesn’t seem to change for play/pause (although wakelock_size does). Plex just looks to be special all around. I’ll do some more digging but it’s a real shame Android TV doesn’t seem to have a standardized way for apps to report media state.

# Home screen
2019-08-04 10:07:13 INFO (SyncWorker_14) [custom_components.androidtv.media_player] Output of command 'GET_PROPERTIES' from 'media_player.adb_den_shield_tv': {'screen_on': True, 'awake': True, 'wake_lock_size': 1, 'current_app': 'com.google.android.tvlauncher', 'media_session_state': None, 'audio_state': 'playing', 'device': 'hdmi', 'is_volume_muted': False, 'volume': None}

# Opened plex, nothing playing
2019-08-04 10:09:02 INFO (SyncWorker_0) [custom_components.androidtv.media_player] Output of command 'GET_PROPERTIES' from 'media_player.adb_den_shield_tv': {'screen_on': True, 'awake': True, 'wake_lock_size': 1, 'current_app': 'com.plexapp.android', 'media_session_state': None, 'audio_state': 'playing', 'device': 'hdmi', 'is_volume_muted': False, 'volume': None}

# Playing a video
2019-08-04 10:10:51 INFO (SyncWorker_10) [custom_components.androidtv.media_player] Output of command 'GET_PROPERTIES' from 'media_player.adb_den_shield_tv': {'screen_on': True, 'awake': True, 'wake_lock_size': 3, 'current_app': 'com.plexapp.android', 'media_session_state': 3, 'audio_state': 'playing', 'device': 'hdmi', 'is_volume_muted': False, 'volume': None}

# Paused
2019-08-04 10:11:06 INFO (SyncWorker_13) [custom_components.androidtv.media_player] Output of command 'GET_PROPERTIES' from 'media_player.adb_den_shield_tv': {'screen_on': True, 'awake': True, 'wake_lock_size': 1, 'current_app': 'com.plexapp.android', 'media_session_state': 3, 'audio_state': 'paused', 'device': 'hdmi', 'is_volume_muted': False, 'volume': None}

# Resumed
2019-08-04 10:11:55 INFO (SyncWorker_3) [custom_components.androidtv.media_player] Output of command 'GET_PROPERTIES' from 'media_player.adb_den_shield_tv': {'screen_on': True, 'awake': True, 'wake_lock_size': 3, 'current_app': 'com.plexapp.android', 'media_session_state': 3, 'audio_state': 'playing', 'device': 'hdmi', 'is_volume_muted': False, 'volume': None}

# Stopped
2019-08-04 10:12:15 INFO (SyncWorker_9) [custom_components.androidtv.media_player] Output of command 'GET_PROPERTIES' from 'media_player.adb_den_shield_tv': {'screen_on': True, 'awake': True, 'wake_lock_size': 1, 'current_app': 'com.plexapp.android', 'media_session_state': None, 'audio_state': 'playing', 'device': 'hdmi', 'is_volume_muted': False, 'volume': None}


Thanks for testing it. The question is, how could that kind of logic be implemented via YAML config… Maybe something like this could work? (I would need to modify the androidtv code.)

...
state_detection_rules:
  'com.plexapp.android':
    - 'playing':
        'media_session_state': 3
        'wake_lock_size': 3
    - 'paused':
        'media_session_state': 3
        'wake_lock_size': 1
    - 'standby'

First, it will report ‘playing’ if media_session_state is 3 and wake_lock_size is 3. If that doesn’t hold true, it will report ‘paused’ if media_session_state is 3 and wake_lock_size is 1. Otherwise, it will report ‘standby’.

What do you think?

Sounds feasible. I wonder how much logic is reasonable to have in the component, that route looks like it would work for this use case but would it add too much complexity? Or perhaps following something along the line of quirks like the ZHA component uses so the logic isn’t kept in the config yaml?

I’m not familiar with the ZHA component.

That’s a valid point about too much complexity, and that’s the reason why I didn’t jump on this idea when it was first suggested in a GitHub issue. But I think the arguments in favor of this approach are:

  • It’s difficult, if not impossible, to include code in the androidtv package that will correctly detect the state for all apps on all devices.
  • I’ve asked for community support with the state detection and tried to make it as easy as possible to contribute, but that hasn’t gotten much traction.
  • Perhaps most importantly, this state_detection_rules config parameter is completely optional. If a user has no issues with how the state detection is working, they don’t need to do anything. But if they want the state detection to work better, this will allow them to fix it on their own instead of just posting on the forum that it doesn’t work.

Also, thanks for posting the output of those GET_PROPERTIES commands. I plan to incorporate that into some unit tests for this custom state detection code.

1 Like

Makes sense. I would suggest that adding a few of the relevant properties (media_session_state, audio_state, and wake_lock_size) as attributes would help debugging and creating better rules. It’s not like the androidtv.adb_command service is all that bad to run, just would be easier to be able to glance at the attributes to see what’s going on.

That’s a good idea, but it would require returning more values from the update method in the androidtv package. I’ll keep that in mind, but first I want to get custom state detection into the official HA component.

I updated the androidtv code and this revised approach is ready for testing.

https://github.com/JeffLIrion/ha-androidtv/tree/custom_state_detection/custom_components/androidtv

The only files you need to update are:

  • custom_components/androidtv/androidtv/basetv.py
  • custom_components/androidtv/androidtv/constants.py
  • custom_components/androidtv/androidtv/firetv.py

Modify your state_detection_rules parameter as above:

state_detection_rules:
  'com.plexapp.android':
    - 'playing':
        'media_session_state': 3
        'wake_lock_size': 3
    - 'paused':
        'media_session_state': 3
        'wake_lock_size': 1
    - 'standby'

That works perfectly!

For the record, I haven’t found any other apps so far that require this level of kludgery. All the rest (Hulu, Vudu, Google Movies, Netflix, Amazon, HBO Now) seem to set media_session_state to 3 when playing and 2 when paused. I’m using the rules to set that with a fallback to idle to prevent using audio_state completely.

Thanks for testing it! I’ll submit a pull request shortly.

Sounds great. One other thought, any chance of being able to set a default set or exclusion for the state detection rules? I’m finding myself setting almost all of my video apps to exclude audio_state with this new Shield update as so:

      - 'media_session_state'
      - 'idle'

It would be nice to be able to just exclude that audio_state somehow by default rather than a bunch of duplicate entries.

You’re welcome to submit pull requests to the androidtv package and the HA component. My thoughts are:

  1. There’s already a lot of config variables for the Android TV component and the documentation is quite lengthy, so I’m reluctant to add more unless they provide a lot of value to the users (and I believe this state_detection_rules parameter does).
  2. Honestly, it’s much easier and less complicated – for both the user and the developers – to just have the user repeat those YAML lines a handful of times. Put it in your config, maybe use a !include, and then never look at it again!:smile:

Another approach to assisting with creating state detection rules would be a simple python script. Something like (not tested!):

# example JSON service data:
# {"entity_id", "media_player.fire_tv", "state": "playing"}

entity_id = data.get('entity_id')
true_state = data.get('state')

hass.services.call('androidtv', 'adb_command', {'entity_id': entity_id, 'command': 'GET_PROPERTIES'})

sleep(1)

adb_response = hass.states.get(entity_id).attributes.get('adb_response')

logging.info('State = {0}: {1}'.format(true_state, adb_response))

Fair enough. I think I’m good, but that python script could be useful as others start using the feature.

This seems to be working pretty great. The stock androidtv component would rarely work for any apps on my 2 FireTVs for me. I think the only app that reported the correct state out of the box was Netflix.

With this setup however, the majority of them seem to work with any custom logic.

Those apps i tested with with were:

com.sling: “Sling”
org.chromium.youtube_apk: “YouTube”
com.netflix.ninja: “Netflix”

Hulu did need some custom logic which I was able to get to report an accurate Playing state with the following:

state_detection_rules:
      'com.hulu.plus':
        - 'playing':
              'wake_lock_size': 3
        - 'paused':
              'media_session_state': 3
              'wake_lock_size': 1
        - 'standby'

for me, when testing with androidtv.adb_command service with a command of GET_PROPERTIES, it would always return ‘None’ for a media_session_state and ‘wake_lock_size’: 3, which the above logic reflects.