Skip to content

Commit

Permalink
add channel_live attribute (#21)
Browse files Browse the repository at this point in the history
* add channel_live attribute

* Update README.md

* Update info.md
  • Loading branch information
pinkywafer authored Jan 14, 2021
1 parent 005105d commit b0fec09
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 130 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ key | type | description
* published: The time and date the video was published
* stream: If the video was streamed live
* live: If the video is live now
* channel_live: If any video on the channel is currently live
***
Expand Down
278 changes: 148 additions & 130 deletions custom_components/youtube/sensor.py
Original file line number Diff line number Diff line change
@@ -1,130 +1,148 @@
"""
A platform which give you info about the newest video on a channel.
For more details about this component, please refer to the documentation at
https://github.com/custom-components/youtube
"""

import html
import logging
import async_timeout
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.helpers.entity import Entity

CONF_CHANNEL_ID = 'channel_id'

ICON = 'mdi:youtube'

BASE_URL = 'https://www.youtube.com/feeds/videos.xml?channel_id={}'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_CHANNEL_ID): cv.string,
})

_LOGGER = logging.getLogger(__name__)


async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None): # pylint: disable=unused-argument
"""Setup sensor platform."""
channel_id = config['channel_id']
session = async_create_clientsession(hass)
try:
url = BASE_URL.format(channel_id)
async with async_timeout.timeout(10, loop=hass.loop):
response = await session.get(url)
info = await response.text()
name = html.parser.HTMLParser().unescape(
info.split('<title>')[1].split('</')[0])
except Exception: # pylint: disable=broad-except

name = None

if name is not None:
async_add_entities([YoutubeSensor(channel_id, name, session)], True)

class YoutubeSensor(Entity):
"""YouTube Sensor class"""
def __init__(self, channel_id, name, session):
self._state = None
self.session = session
self._image = None
self.stream = False
self.live = False
self._name = name
self.channel_id = channel_id
self.url = None
self.published = None

async def async_update(self):
"""Update sensor."""
_LOGGER.debug('%s - Running update', self._name)
try:
url = BASE_URL.format(self.channel_id)
async with async_timeout.timeout(10, loop=self.hass.loop):
response = await self.session.get(url)
info = await response.text()
title = html.parser.HTMLParser().unescape(
info.split('<title>')[2].split('</')[0])
url = info.split('<link rel="alternate" href="')[2].split('"/>')[0]
if self.live or url != self.url:
self.stream, self.live = await is_live(url, self._name, self.hass, self.session)
else:
_LOGGER.debug('%s - Skipping live check', self._name)
self.url = url
self.published = info.split('<published>')[2].split('</')[0]
thumbnail_url = info.split(
'<media:thumbnail url="')[1].split('"')[0]
self._state = title
self._image = thumbnail_url
except Exception as error: # pylint: disable=broad-except
_LOGGER.debug('%s - Could not update - %s', self._name, error)

@property
def name(self):
"""Name."""
return self._name

@property
def entity_picture(self):
"""Picture."""
return self._image

@property
def state(self):
"""State."""
return self._state

@property
def icon(self):
"""Icon."""
return ICON

@property
def device_state_attributes(self):
"""Attributes."""
return {'url': self.url,
'published': self.published,
'stream': self.stream,
'live': self.live}


async def is_live(url, name, hass, session):
"""Return bool if video is stream and bool if video is live"""
live = False
stream = False
try:
async with async_timeout.timeout(10, loop=hass.loop):
response = await session.get(url)
info = await response.text()
if 'isLiveBroadcast' in info:
stream = True
if 'endDate' not in info:
live = True
_LOGGER.debug('%s - Latest Video is live', name)
except Exception as error: # pylint: disable=broad-except
_LOGGER.debug('%s - Could not update - %s', name, error)
return stream, live
"""
A platform which give you info about the newest video on a channel.
For more details about this component, please refer to the documentation at
https://github.com/custom-components/youtube
"""

import html
import logging
import async_timeout
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.helpers.entity import Entity

CONF_CHANNEL_ID = 'channel_id'

ICON = 'mdi:youtube'

BASE_URL = 'https://www.youtube.com/feeds/videos.xml?channel_id={}'
CHANNEL_LIVE_URL = 'https://www.youtube.com/channel/{}/live'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_CHANNEL_ID): cv.string,
})

_LOGGER = logging.getLogger(__name__)


async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None): # pylint: disable=unused-argument
"""Setup sensor platform."""
channel_id = config['channel_id']
session = async_create_clientsession(hass)
try:
url = BASE_URL.format(channel_id)
async with async_timeout.timeout(10, loop=hass.loop):
response = await session.get(url)
info = await response.text()
name = html.parser.HTMLParser().unescape(
info.split('<title>')[1].split('</')[0])
except Exception: # pylint: disable=broad-except

name = None

if name is not None:
async_add_entities([YoutubeSensor(channel_id, name, session)], True)

class YoutubeSensor(Entity):
"""YouTube Sensor class"""
def __init__(self, channel_id, name, session):
self._state = None
self.session = session
self._image = None
self.stream = False
self.live = False
self._name = name
self.channel_id = channel_id
self.url = None
self.published = None
self.channel_live = False

async def async_update(self):
"""Update sensor."""
_LOGGER.debug('%s - Running update', self._name)
try:
url = BASE_URL.format(self.channel_id)
async with async_timeout.timeout(10, loop=self.hass.loop):
response = await self.session.get(url)
info = await response.text()
title = html.parser.HTMLParser().unescape(
info.split('<title>')[2].split('</')[0])
url = info.split('<link rel="alternate" href="')[2].split('"/>')[0]
if self.live or url != self.url:
self.stream, self.live = await is_live(url, self._name, self.hass, self.session)
else:
_LOGGER.debug('%s - Skipping live check', self._name)
self.url = url
self.published = info.split('<published>')[2].split('</')[0]
thumbnail_url = info.split(
'<media:thumbnail url="')[1].split('"')[0]
self._state = title
self._image = thumbnail_url
url = CHANNEL_LIVE_URL.format(self.channel_id)
self.channel_live = await is_channel_live(url, self.name, self.hass, self.session)
except Exception as error: # pylint: disable=broad-except
_LOGGER.debug('%s - Could not update - %s', self._name, error)

@property
def name(self):
"""Name."""
return self._name

@property
def entity_picture(self):
"""Picture."""
return self._image

@property
def state(self):
"""State."""
return self._state

@property
def icon(self):
"""Icon."""
return ICON

@property
def device_state_attributes(self):
"""Attributes."""
return {'url': self.url,
'published': self.published,
'stream': self.stream,
'live': self.live,
'channel_is_live': self.channel_live}

async def is_live(url, name, hass, session):
"""Return bool if video is stream and bool if video is live"""
live = False
stream = False
try:
async with async_timeout.timeout(10, loop=hass.loop):
response = await session.get(url)
info = await response.text()
if 'isLiveBroadcast' in info:
stream = True
if 'endDate' not in info:
live = True
_LOGGER.debug('%s - Latest Video is live', name)
except Exception as error: # pylint: disable=broad-except
_LOGGER.debug('%s - Could not update - %s', name, error)
return stream, live

async def is_channel_live(url, name, hass, session):
"""Return bool if channel is live"""
live = False
try:
async with async_timeout.timeout(10, loop=hass.loop):
response = await session.get(url)
info = await response.text()
if '"isLive":true' in info:
live = True
_LOGGER.debug('%s - Channel is live', name)
except Exception as error: # pylint: disable=broad-except
_LOGGER.debug('%s - Could not update - %s', name, error)
return live
1 change: 1 addition & 0 deletions info.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ key | type | description
* published: The time and date the video was published
* stream: If the video was streamed live
* live: If the video is live now
* channel_live: If any video on the channel is live

0 comments on commit b0fec09

Please sign in to comment.