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

Add WebOS LG Tv current program name and watching progress #12166

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
171 changes: 135 additions & 36 deletions homeassistant/components/media_player/webostv.py
Expand Up @@ -5,13 +5,14 @@
https://home-assistant.io/components/media_player.webostv/
"""
import asyncio
from datetime import timedelta
from datetime import timedelta, datetime
import logging
from urllib.parse import urlparse

# pylint: disable=unused-import
from typing import Dict # noqa: F401

import pytz
import voluptuous as vol

from homeassistant.components.media_player import (
Expand Down Expand Up @@ -139,7 +140,7 @@ def request_configuration(
return

# pylint: disable=unused-argument
def lgtv_configuration_callback(data):
def lgtv_configuration_callback():
"""Handle actions when configuration callback is called."""
setup_tv(host, name, customize, config, timeout, hass,
add_devices, turn_on_action)
Expand Down Expand Up @@ -174,7 +175,19 @@ def __init__(self, host, name, customize, config, timeout,
self._state = STATE_UNKNOWN
self._source_list = {}
self._app_list = {}
self._channel = None

self.reset_channel_info()
self._progress_updated_at = util.dt.utcnow()

@property
def app_id(self):
"""ID of the current running app."""
return self._current_source_id

@property
def app_name(self):
"""Name of the current running app."""
return self._current_source

@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update(self):
Expand All @@ -190,45 +203,23 @@ def update(self):
self._state = STATE_OFF
self._current_source = None
self._current_source_id = None
self._channel = None
self.reset_channel_info()

if self._state is not STATE_OFF:
self._muted = self._client.get_muted()
self._volume = self._client.get_volume()
self._channel = self._client.get_current_channel()

self._source_list = {}
self._app_list = {}
conf_sources = self._customize.get(CONF_SOURCES, [])

for app in self._client.get_apps():
self._app_list[app['id']] = app
if app['id'] == self._current_source_id:
self._current_source = app['title']
self._source_list[app['title']] = app
elif (not conf_sources or
app['id'] in conf_sources or
any(word in app['title']
for word in conf_sources) or
any(word in app['id']
for word in conf_sources)):
self._source_list[app['title']] = app

for source in self._client.get_inputs():
if source['id'] == self._current_source_id:
self._current_source = source['label']
self._source_list[source['label']] = source
elif (not conf_sources or
source['label'] in conf_sources or
any(source['label'].find(word) != -1
for word in conf_sources)):
self._source_list[source['label']] = source

self.update_channel_info()
self.update_apps_and_sources()

except (OSError, ConnectionClosed, TypeError,
asyncio.TimeoutError):
self._state = STATE_OFF
self._current_source = None
self._current_source_id = None
self._channel = None
self.reset_channel_info()

self._progress_updated_at = util.dt.utcnow()

@property
def name(self):
Expand Down Expand Up @@ -268,11 +259,38 @@ def media_content_type(self):
@property
def media_title(self):
"""Title of current playing media."""
if (self._channel is not None) and ('channelName' in self._channel):
return self._channel['channelName']
else:
return self._title

@property
def media_channel(self):
"""Channel currently playing."""
return self._channel

@property
def media_position(self):
"""Position of current playing media in seconds."""
if self._progress is None:
return None

position = self._progress

if self._state == STATE_PLAYING:
position += (util.dt.utcnow() -
Copy link
Member

Choose a reason for hiding this comment

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

No, this is not needed. This property returns only the stored progress pointer. The frontend will do this based on the media_position_updated_at. This is to avoid unnecessary state updates.

self._progress_updated_at).total_seconds()

return position

@ property
def media_position_updated_at(self):
"""When was the position of the current playing media valid."""
if self.state == STATE_PLAYING:
return self._progress_updated_at

@ property
def media_duration(self):
"""Duration of current playing media in seconds."""
return self._duration

@property
def media_image_url(self):
"""Image url of current playing media."""
Expand Down Expand Up @@ -363,3 +381,84 @@ def media_next_track(self):
def media_previous_track(self):
"""Send the previous track command."""
self._client.rewind()

def play_media(self, media_type, media_id, **kwargs):
"""Play a piece of media."""
self._playing = True
self._state = STATE_PLAYING
if media_type != MEDIA_TYPE_CHANNEL:
self._client.play()
else:
self._client.set_channel(media_id)

def update_channel_info(self):
channel = self._client.get_current_channel()

# no need to updated channel info while we are not watching TV
if channel.get('returnValue') is None:
self.reset_channel_info()
return

self._channel = channel.get('channelName')
self._title = self._channel
self._duration = None
channel_id = channel.get('channelId')
self._progress_updated_at = util.dt.utcnow()
Copy link
Member

Choose a reason for hiding this comment

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

This should not be updated on each call. It should only be updated if the source is changing. Otherwise we keep getting unnecessary state updates on each update

info = self._client.get_channel_info()

# tv returns the list of all channels
now = datetime.now(pytz.UTC)
Copy link
Member

Choose a reason for hiding this comment

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

Use homeassistant.util.dt.utcnow()

for program in info.get('programList', []):
# skip any invalid data
if program['startTime'] == '' or program['endTime'] == '':
continue

st = self._date_helper(program['startTime'])
ed = self._date_helper(program['endTime'])
if program['channelId'] == channel_id and st <= now < ed:
self._title = "{}: {}".format(
self._channel,
program['programName']
)
self._duration = program['duration']
self._progress = (now - st).total_seconds()
Copy link
Member

Choose a reason for hiding this comment

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

Don't update progress unless channel changed. Also, only when you update this property can you update the progress_updated_at property

break

def reset_channel_info(self):
self._channel = None
self._title = None
self._duration = None
self._progress = None

def update_apps_and_sources(self):
self._source_list = {}
self._app_list = {}
conf_sources = self._customize.get(CONF_SOURCES, [])

for app in self._client.get_apps():
self._app_list[app['id']] = app
if app['id'] == self._current_source_id:
self._current_source = app['title']
self._source_list[app['title']] = app
elif (not conf_sources or
app['id'] in conf_sources or
any(word in app['title']
for word in conf_sources) or
any(word in app['id']
for word in conf_sources)):
self._source_list[app['title']] = app

for source in self._client.get_inputs():
if source['id'] == self._current_source_id:
self._current_source = source['label']
self._source_list[source['label']] = source
elif (not conf_sources or
source['label'] in conf_sources or
any(source['label'].find(word) != -1
for word in conf_sources)):
self._source_list[source['label']] = source

def _date_helper(self, date_str):
return pytz.utc.localize(
Copy link
Member

Choose a reason for hiding this comment

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

Don't use localize. Use the helpers in homeassistant.util.dt.

datetime.strptime(date_str, '%Y,%m,%d,%H,%M,%S')
)