Make Yi platform async (#14944)

* Conversion complete

* Updated requirements

* Got rid of 3.6-specific syntax

* Removed more 3.6-specific syntax

* Contributor-requested changes
This commit is contained in:
Aaron Bach 2018-06-13 09:00:33 -06:00 committed by Paulus Schoutsen
parent 08adfd87f7
commit d549e26a9b
2 changed files with 62 additions and 50 deletions

View file

@ -11,11 +11,13 @@ import voluptuous as vol
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.components.ffmpeg import DATA_FFMPEG
from homeassistant.const import (CONF_HOST, CONF_NAME, CONF_PATH,
CONF_PASSWORD, CONF_PORT, CONF_USERNAME)
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_PATH, CONF_PASSWORD, CONF_PORT, CONF_USERNAME)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
from homeassistant.exceptions import PlatformNotReady
REQUIREMENTS = ['aioftp==0.10.1']
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
@ -38,12 +40,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
async def async_setup_platform(hass,
config,
async_add_devices,
discovery_info=None):
async def async_setup_platform(
hass, config, async_add_devices, discovery_info=None):
"""Set up a Yi Camera."""
_LOGGER.debug('Received configuration: %s', config)
async_add_devices([YiCamera(hass, config)], True)
@ -54,71 +53,81 @@ class YiCamera(Camera):
"""Initialize."""
super().__init__()
self._extra_arguments = config.get(CONF_FFMPEG_ARGUMENTS)
self._ftp = None
self._last_image = None
self._last_url = None
self._manager = hass.data[DATA_FFMPEG]
self._name = config.get(CONF_NAME)
self.host = config.get(CONF_HOST)
self.port = config.get(CONF_PORT)
self.path = config.get(CONF_PATH)
self.user = config.get(CONF_USERNAME)
self.passwd = config.get(CONF_PASSWORD)
self._name = config[CONF_NAME]
self.host = config[CONF_HOST]
self.port = config[CONF_PORT]
self.path = config[CONF_PATH]
self.user = config[CONF_USERNAME]
self.passwd = config[CONF_PASSWORD]
@property
def name(self):
"""Return the name of this camera."""
return self._name
hass.async_add_job(self._connect_to_client)
@property
def brand(self):
"""Camera brand."""
return DEFAULT_BRAND
def get_latest_video_url(self):
@property
def name(self):
"""Return the name of this camera."""
return self._name
async def _connect_to_client(self):
"""Attempt to establish a connection via FTP."""
from aioftp import Client, StatusCodeError
ftp = Client()
try:
await ftp.connect(self.host)
await ftp.login(self.user, self.passwd)
self._ftp = ftp
except StatusCodeError as err:
raise PlatformNotReady(err)
async def _get_latest_video_url(self):
"""Retrieve the latest video file from the customized Yi FTP server."""
from ftplib import FTP, error_perm
ftp = FTP(self.host)
try:
ftp.login(self.user, self.passwd)
except error_perm as exc:
_LOGGER.error('There was an error while logging into the camera')
_LOGGER.debug(exc)
return False
from aioftp import StatusCodeError
try:
ftp.cwd(self.path)
except error_perm as exc:
_LOGGER.error('Unable to find path: %s', self.path)
_LOGGER.debug(exc)
return False
await self._ftp.change_directory(self.path)
dirs = []
for path, attrs in await self._ftp.list():
if attrs['type'] == 'dir' and '.' not in str(path):
dirs.append(path)
latest_dir = dirs[-1]
await self._ftp.change_directory(latest_dir)
dirs = [d for d in ftp.nlst() if '.' not in d]
if not dirs:
_LOGGER.warning("There don't appear to be any uploaded videos")
return False
videos = []
for path, _ in await self._ftp.list():
videos.append(path)
if not videos:
_LOGGER.info('Video folder "%s" empty; delaying', latest_dir)
return None
latest_dir = dirs[-1]
ftp.cwd(latest_dir)
videos = ftp.nlst()
if not videos:
_LOGGER.info('Video folder "%s" is empty; delaying', latest_dir)
return False
return 'ftp://{0}:{1}@{2}:{3}{4}/{5}/{6}'.format(
self.user, self.passwd, self.host, self.port, self.path,
latest_dir, videos[-1])
return 'ftp://{0}:{1}@{2}:{3}{4}/{5}/{6}'.format(
self.user, self.passwd, self.host, self.port, self.path,
latest_dir, videos[-1])
except (ConnectionRefusedError, StatusCodeError) as err:
_LOGGER.error('Error while fetching video: %s', err)
return None
async def async_camera_image(self):
"""Return a still image response from the camera."""
from haffmpeg import ImageFrame, IMAGE_JPEG
url = await self.hass.async_add_job(self.get_latest_video_url)
url = await self._get_latest_video_url()
if url != self._last_url:
ffmpeg = ImageFrame(self._manager.binary, loop=self.hass.loop)
self._last_image = await asyncio.shield(ffmpeg.get_image(
url, output_format=IMAGE_JPEG,
extra_cmd=self._extra_arguments), loop=self.hass.loop)
self._last_image = await asyncio.shield(
ffmpeg.get_image(
url,
output_format=IMAGE_JPEG,
extra_cmd=self._extra_arguments),
loop=self.hass.loop)
self._last_url = url
return self._last_image

View file

@ -84,6 +84,9 @@ aiodns==1.1.1
# homeassistant.components.device_tracker.freebox
aiofreepybox==0.0.3
# homeassistant.components.camera.yi
aioftp==0.10.1
# homeassistant.components.emulated_hue
# homeassistant.components.http
aiohttp_cors==0.7.0