diff --git a/.coveragerc b/.coveragerc
index 0defebb7e7a..e4d896e444b 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -11,6 +11,9 @@ omit =
homeassistant/components/alarmdecoder.py
homeassistant/components/*/alarmdecoder.py
+ homeassistant/components/amcrest.py
+ homeassistant/components/*/amcrest.py
+
homeassistant/components/apcupsd.py
homeassistant/components/*/apcupsd.py
@@ -220,7 +223,6 @@ omit =
homeassistant/components/binary_sensor/rest.py
homeassistant/components/binary_sensor/tapsaff.py
homeassistant/components/browser.py
- homeassistant/components/camera/amcrest.py
homeassistant/components/camera/bloomsky.py
homeassistant/components/camera/ffmpeg.py
homeassistant/components/camera/foscam.py
@@ -388,7 +390,6 @@ omit =
homeassistant/components/remote/itach.py
homeassistant/components/scene/hunterdouglas_powerview.py
homeassistant/components/scene/lifx_cloud.py
- homeassistant/components/sensor/amcrest.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/arwn.py
homeassistant/components/sensor/bbox.py
diff --git a/homeassistant/components/amcrest.py b/homeassistant/components/amcrest.py
new file mode 100644
index 00000000000..8a40c790c12
--- /dev/null
+++ b/homeassistant/components/amcrest.py
@@ -0,0 +1,149 @@
+"""
+This component provides basic support for Amcrest IP cameras.
+
+For more details about this component, please refer to the documentation at
+https://home-assistant.io/components/amcrest/
+"""
+import logging
+from datetime import timedelta
+
+import aiohttp
+import voluptuous as vol
+from requests.exceptions import HTTPError, ConnectTimeout
+
+import homeassistant.loader as loader
+from homeassistant.const import (
+ CONF_NAME, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD,
+ CONF_SENSORS, CONF_SCAN_INTERVAL, HTTP_BASIC_AUTHENTICATION)
+from homeassistant.helpers import discovery
+import homeassistant.helpers.config_validation as cv
+
+REQUIREMENTS = ['amcrest==1.2.0']
+DEPENDENCIES = ['ffmpeg']
+
+_LOGGER = logging.getLogger(__name__)
+
+CONF_AUTHENTICATION = 'authentication'
+CONF_RESOLUTION = 'resolution'
+CONF_STREAM_SOURCE = 'stream_source'
+CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
+
+DEFAULT_NAME = 'Amcrest Camera'
+DEFAULT_PORT = 80
+DEFAULT_RESOLUTION = 'high'
+DEFAULT_STREAM_SOURCE = 'snapshot'
+TIMEOUT = 10
+
+DATA_AMCREST = 'amcrest'
+DOMAIN = 'amcrest'
+
+NOTIFICATION_ID = 'amcrest_notification'
+NOTIFICATION_TITLE = 'Amcrest Camera Setup'
+
+RESOLUTION_LIST = {
+ 'high': 0,
+ 'low': 1,
+}
+
+SCAN_INTERVAL = timedelta(seconds=10)
+
+AUTHENTICATION_LIST = {
+ 'basic': 'basic'
+}
+
+STREAM_SOURCE_LIST = {
+ 'mjpeg': 0,
+ 'snapshot': 1,
+ 'rtsp': 2,
+}
+
+# Sensor types are defined like: Name, units, icon
+SENSORS = {
+ 'motion_detector': ['Motion Detected', None, 'mdi:run'],
+ 'sdcard': ['SD Used', '%', 'mdi:sd'],
+ 'ptz_preset': ['PTZ Preset', None, 'mdi:camera-iris'],
+}
+
+CONFIG_SCHEMA = vol.Schema({
+ DOMAIN: vol.All(cv.ensure_list, [vol.Schema({
+ vol.Required(CONF_HOST): cv.string,
+ vol.Required(CONF_USERNAME): cv.string,
+ vol.Required(CONF_PASSWORD): cv.string,
+ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
+ vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
+ vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION):
+ vol.All(vol.In(AUTHENTICATION_LIST)),
+ vol.Optional(CONF_RESOLUTION, default=DEFAULT_RESOLUTION):
+ vol.All(vol.In(RESOLUTION_LIST)),
+ vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE):
+ vol.All(vol.In(STREAM_SOURCE_LIST)),
+ vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
+ vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL):
+ cv.time_period,
+ vol.Optional(CONF_SENSORS, default=None):
+ vol.All(cv.ensure_list, [vol.In(SENSORS)]),
+ })])
+}, extra=vol.ALLOW_EXTRA)
+
+
+def setup(hass, config):
+ """Set up the Amcrest IP Camera component."""
+ from amcrest import AmcrestCamera
+
+ amcrest_cams = config[DOMAIN]
+
+ persistent_notification = loader.get_component('persistent_notification')
+ for device in amcrest_cams:
+ camera = AmcrestCamera(device.get(CONF_HOST),
+ device.get(CONF_PORT),
+ device.get(CONF_USERNAME),
+ device.get(CONF_PASSWORD)).camera
+ try:
+ camera.current_time
+
+ except (ConnectTimeout, HTTPError) as ex:
+ _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex))
+ persistent_notification.create(
+ hass, 'Error: {}
'
+ 'You will need to restart hass after fixing.'
+ ''.format(ex),
+ title=NOTIFICATION_TITLE,
+ notification_id=NOTIFICATION_ID)
+ return False
+
+ ffmpeg_arguments = device.get(CONF_FFMPEG_ARGUMENTS)
+ name = device.get(CONF_NAME)
+ resolution = RESOLUTION_LIST[device.get(CONF_RESOLUTION)]
+ sensors = device.get(CONF_SENSORS)
+ stream_source = STREAM_SOURCE_LIST[device.get(CONF_STREAM_SOURCE)]
+
+ username = device.get(CONF_USERNAME)
+ password = device.get(CONF_PASSWORD)
+
+ # currently aiohttp only works with basic authentication
+ # only valid for mjpeg streaming
+ if username is not None and password is not None:
+ if device.get(CONF_AUTHENTICATION) == HTTP_BASIC_AUTHENTICATION:
+ authentication = aiohttp.BasicAuth(username, password)
+ else:
+ authentication = None
+
+ discovery.load_platform(
+ hass, 'camera', DOMAIN, {
+ 'device': camera,
+ CONF_AUTHENTICATION: authentication,
+ CONF_FFMPEG_ARGUMENTS: ffmpeg_arguments,
+ CONF_NAME: name,
+ CONF_RESOLUTION: resolution,
+ CONF_STREAM_SOURCE: stream_source,
+ }, config)
+
+ if sensors:
+ discovery.load_platform(
+ hass, 'sensor', DOMAIN, {
+ 'device': camera,
+ CONF_NAME: name,
+ CONF_SENSORS: sensors,
+ }, config)
+
+ return True
diff --git a/homeassistant/components/camera/amcrest.py b/homeassistant/components/camera/amcrest.py
index 8f8b7e5f9f5..51b8ff13906 100644
--- a/homeassistant/components/camera/amcrest.py
+++ b/homeassistant/components/camera/amcrest.py
@@ -7,108 +7,59 @@ https://home-assistant.io/components/camera.amcrest/
import asyncio
import logging
-import aiohttp
-import voluptuous as vol
-
-import homeassistant.loader as loader
-from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
+from homeassistant.components.amcrest import (
+ STREAM_SOURCE_LIST, TIMEOUT)
+from homeassistant.components.camera import Camera
from homeassistant.components.ffmpeg import DATA_FFMPEG
-from homeassistant.const import (
- CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT)
-from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_web,
async_aiohttp_proxy_stream)
-REQUIREMENTS = ['amcrest==1.2.0']
-DEPENDENCIES = ['ffmpeg']
+DEPENDENCIES = ['amcrest', 'ffmpeg']
_LOGGER = logging.getLogger(__name__)
-CONF_RESOLUTION = 'resolution'
-CONF_STREAM_SOURCE = 'stream_source'
-CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
-DEFAULT_NAME = 'Amcrest Camera'
-DEFAULT_PORT = 80
-DEFAULT_RESOLUTION = 'high'
-DEFAULT_STREAM_SOURCE = 'mjpeg'
-
-NOTIFICATION_ID = 'amcrest_notification'
-NOTIFICATION_TITLE = 'Amcrest Camera Setup'
-
-RESOLUTION_LIST = {
- 'high': 0,
- 'low': 1,
-}
-
-STREAM_SOURCE_LIST = {
- 'mjpeg': 0,
- 'snapshot': 1,
- 'rtsp': 2,
-}
-
-CONTENT_TYPE_HEADER = 'Content-Type'
-TIMEOUT = 5
-
-PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
- vol.Required(CONF_HOST): cv.string,
- vol.Required(CONF_USERNAME): cv.string,
- vol.Required(CONF_PASSWORD): cv.string,
- vol.Optional(CONF_RESOLUTION, default=DEFAULT_RESOLUTION):
- vol.All(vol.In(RESOLUTION_LIST)),
- vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
- vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
- vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE):
- vol.All(vol.In(STREAM_SOURCE_LIST)),
- vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
-})
-
-
-def setup_platform(hass, config, add_devices, discovery_info=None):
+@asyncio.coroutine
+def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up an Amcrest IP Camera."""
- from amcrest import AmcrestCamera
- camera = AmcrestCamera(
- config.get(CONF_HOST), config.get(CONF_PORT),
- config.get(CONF_USERNAME), config.get(CONF_PASSWORD)).camera
+ if discovery_info is None:
+ return
- persistent_notification = loader.get_component('persistent_notification')
- try:
- camera.current_time
- # pylint: disable=broad-except
- except Exception as ex:
- _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex))
- persistent_notification.create(
- hass, 'Error: {}
'
- 'You will need to restart hass after fixing.'
- ''.format(ex),
- title=NOTIFICATION_TITLE,
- notification_id=NOTIFICATION_ID)
- return False
+ device = discovery_info['device']
+ authentication = discovery_info['authentication']
+ ffmpeg_arguments = discovery_info['ffmpeg_arguments']
+ name = discovery_info['name']
+ resolution = discovery_info['resolution']
+ stream_source = discovery_info['stream_source']
+
+ async_add_devices([
+ AmcrestCam(hass,
+ name,
+ device,
+ authentication,
+ ffmpeg_arguments,
+ stream_source,
+ resolution)], True)
- add_devices([AmcrestCam(hass, config, camera)])
return True
class AmcrestCam(Camera):
"""An implementation of an Amcrest IP camera."""
- def __init__(self, hass, device_info, camera):
+ def __init__(self, hass, name, camera, authentication,
+ ffmpeg_arguments, stream_source, resolution):
"""Initialize an Amcrest camera."""
super(AmcrestCam, self).__init__()
+ self._name = name
self._camera = camera
self._base_url = self._camera.get_base_url()
- self._name = device_info.get(CONF_NAME)
self._ffmpeg = hass.data[DATA_FFMPEG]
- self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS)
- self._resolution = RESOLUTION_LIST[device_info.get(CONF_RESOLUTION)]
- self._stream_source = STREAM_SOURCE_LIST[
- device_info.get(CONF_STREAM_SOURCE)
- ]
- self._token = self._auth = aiohttp.BasicAuth(
- device_info.get(CONF_USERNAME),
- password=device_info.get(CONF_PASSWORD)
- )
+ self._ffmpeg_arguments = ffmpeg_arguments
+ self._stream_source = stream_source
+ self._resolution = resolution
+ self._token = self._auth = authentication
def camera_image(self):
"""Return a still image reponse from the camera."""
diff --git a/homeassistant/components/sensor/amcrest.py b/homeassistant/components/sensor/amcrest.py
index 23f7fc4dfbe..e7bf309c33a 100644
--- a/homeassistant/components/sensor/amcrest.py
+++ b/homeassistant/components/sensor/amcrest.py
@@ -4,92 +4,50 @@ This component provides HA sensor support for Amcrest IP cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.amcrest/
"""
+import asyncio
from datetime import timedelta
import logging
-import voluptuous as vol
-import homeassistant.helpers.config_validation as cv
-
-from homeassistant.components.sensor import PLATFORM_SCHEMA
-from homeassistant.const import (
- CONF_HOST, CONF_NAME, CONF_MONITORED_CONDITIONS,
- CONF_USERNAME, CONF_PASSWORD, CONF_PORT, STATE_UNKNOWN)
+from homeassistant.components.amcrest import SENSORS
from homeassistant.helpers.entity import Entity
-import homeassistant.loader as loader
+from homeassistant.const import STATE_UNKNOWN
-from requests.exceptions import HTTPError, ConnectTimeout
-
-REQUIREMENTS = ['amcrest==1.2.0']
+DEPENDENCIES = ['amcrest']
_LOGGER = logging.getLogger(__name__)
-NOTIFICATION_ID = 'amcrest_notification'
-NOTIFICATION_TITLE = 'Amcrest Sensor Setup'
-
-DEFAULT_NAME = 'Amcrest'
-DEFAULT_PORT = 80
SCAN_INTERVAL = timedelta(seconds=10)
-# Sensor types are defined like: Name, units, icon
-SENSOR_TYPES = {
- 'motion_detector': ['Motion Detected', None, 'run'],
- 'sdcard': ['SD Used', '%', 'sd'],
- 'ptz_preset': ['PTZ Preset', None, 'camera-iris'],
-}
-PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
- vol.Required(CONF_HOST): cv.string,
- vol.Required(CONF_USERNAME): cv.string,
- vol.Required(CONF_PASSWORD): cv.string,
- vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
- vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
- vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
- vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
-})
-
-
-def setup_platform(hass, config, add_devices, discovery_info=None):
+@asyncio.coroutine
+def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up a sensor for an Amcrest IP Camera."""
- from amcrest import AmcrestCamera
+ if discovery_info is None:
+ return
- camera = AmcrestCamera(
- config.get(CONF_HOST), config.get(CONF_PORT),
- config.get(CONF_USERNAME), config.get(CONF_PASSWORD)).camera
+ device = discovery_info['device']
+ name = discovery_info['name']
+ sensors = discovery_info['sensors']
- persistent_notification = loader.get_component('persistent_notification')
- try:
- camera.current_time
- except (ConnectTimeout, HTTPError) as ex:
- _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex))
- persistent_notification.create(
- hass, 'Error: {}
'
- 'You will need to restart hass after fixing.'
- ''.format(ex),
- title=NOTIFICATION_TITLE,
- notification_id=NOTIFICATION_ID)
- return False
-
- sensors = []
- for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
- sensors.append(AmcrestSensor(config, camera, sensor_type))
-
- add_devices(sensors, True)
+ amcrest_sensors = []
+ for sensor_type in sensors:
+ amcrest_sensors.append(AmcrestSensor(name, device, sensor_type))
+ async_add_devices(amcrest_sensors, True)
return True
class AmcrestSensor(Entity):
"""A sensor implementation for Amcrest IP camera."""
- def __init__(self, device_info, camera, sensor_type):
+ def __init__(self, name, camera, sensor_type):
"""Initialize a sensor for Amcrest camera."""
- super(AmcrestSensor, self).__init__()
self._attrs = {}
self._camera = camera
self._sensor_type = sensor_type
- self._name = '{0}_{1}'.format(device_info.get(CONF_NAME),
- SENSOR_TYPES.get(self._sensor_type)[0])
- self._icon = 'mdi:{}'.format(SENSOR_TYPES.get(self._sensor_type)[2])
+ self._name = '{0}_{1}'.format(name,
+ SENSORS.get(self._sensor_type)[0])
+ self._icon = 'mdi:{}'.format(SENSORS.get(self._sensor_type)[2])
self._state = STATE_UNKNOWN
@property
@@ -115,7 +73,7 @@ class AmcrestSensor(Entity):
@property
def unit_of_measurement(self):
"""Return the units of measurement."""
- return SENSOR_TYPES.get(self._sensor_type)[1]
+ return SENSORS.get(self._sensor_type)[1]
def update(self):
"""Get the latest data and updates the state."""
diff --git a/requirements_all.txt b/requirements_all.txt
index 80eb6987ce8..5d09b63df80 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -60,8 +60,7 @@ aiopvapi==1.4
# homeassistant.components.alarmdecoder
alarmdecoder==0.12.1.0
-# homeassistant.components.camera.amcrest
-# homeassistant.components.sensor.amcrest
+# homeassistant.components.amcrest
amcrest==1.2.0
# homeassistant.components.media_player.anthemav