From aec25c88b42d8471c084656504e0a5da287ce7a1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 10 Jul 2015 23:17:12 -0700 Subject: [PATCH] Clean up camera component --- homeassistant/components/camera/__init__.py | 82 ++++-------- homeassistant/components/camera/generic.py | 139 ++++---------------- 2 files changed, 54 insertions(+), 167 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 200e48f4862..38379e67e24 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -5,24 +5,23 @@ homeassistant.components.camera Component to interface with various cameras. The following features are supported: --Recording --Snapshot --Motion Detection Recording(for supported cameras) --Automatic Configuration(for supported cameras) --Creation of child entities for supported functions --Collating motion event images passed via FTP into time based events --Returning recorded camera images and streams --Proxying image requests via HA for external access --Converting a still image url into a live video stream --A service for calling camera functions + - Returning recorded camera images and streams + - Proxying image requests via HA for external access + - Converting a still image url into a live video stream Upcoming features --Camera movement(panning) --Zoom --Light/Nightvision toggling --Support for more devices --A demo entity --Expanded documentation + - Recording + - Snapshot + - Motion Detection Recording(for supported cameras) + - Automatic Configuration(for supported cameras) + - Creation of child entities for supported functions + - Collating motion event images passed via FTP into time based events + - A service for calling camera functions + - Camera movement(panning) + - Zoom + - Light/Nightvision toggling + - Support for more devices + - Expanded documentation """ import requests import logging @@ -103,7 +102,7 @@ def setup(hass, config): camera = component.entities[entity_id] if camera: - response = camera.get_camera_image() + response = camera.camera_image() handler.wfile.write(response) else: handler.send_response(HTTP_NOT_FOUND) @@ -146,7 +145,7 @@ def setup(hass, config): while True: - img_bytes = camera.get_camera_image() + img_bytes = camera.camera_image() headers_str = '\r\n'.join(( 'Content-length: {}'.format(len(img_bytes)), @@ -180,23 +179,15 @@ def setup(hass, config): class Camera(Entity): """ The base class for camera components """ + def __init__(self): + self.is_streaming = False + @property # pylint: disable=no-self-use def is_recording(self): """ Returns true if the device is recording """ return False - @property - # pylint: disable=no-self-use - def is_streaming(self): - """ Returns true if the device is streaming """ - return False - - @is_streaming.setter - def is_streaming(self, value): - """ Set this to true when streaming begins """ - pass - @property # pylint: disable=no-self-use def brand(self): @@ -209,19 +200,7 @@ class Camera(Entity): """ Returns string of camera model """ return None - @property - # pylint: disable=no-self-use - def image_url(self): - """ Return the still image segment of the URL """ - return None - - @property - # pylint: disable=no-self-use - def still_image_url(self): - """ Get the URL of a camera still image """ - return None - - def get_camera_image(self): + def camera_image(self): """ Return bytes of camera image """ raise NotImplementedError() @@ -238,14 +217,11 @@ class Camera(Entity): @property def state_attributes(self): """ Returns optional state attributes. """ - attr = super().state_attributes - attr['model_name'] = self.model - attr['brand'] = self.brand - CAMERA_PROXY_URL.format(self.entity_id) - attr['still_image_url'] = CAMERA_STILL_URL.format(self.entity_id) - attr[ATTR_ENTITY_PICTURE] = ENTITY_IMAGE_URL.format( - self.entity_id, - str(time.time())) - attr['stream_url'] = CAMERA_PROXY_URL.format(self.entity_id) - - return attr + return { + 'model_name': self.model, + 'brand': self.brand, + 'still_image_url': CAMERA_STILL_URL.format(self.entity_id), + ATTR_ENTITY_PICTURE: ENTITY_IMAGE_URL.format( + self.entity_id, str(time.time())), + 'stream_url': CAMERA_PROXY_URL.format(self.entity_id) + } diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/camera/generic.py index fa5ac31b9b7..ed6c4c8c3b8 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -1,76 +1,45 @@ """ Support for IP Cameras. -This component provides basic support for IP camera models that do not have -a speicifc HA component. +This component provides basic support for IP cameras. For the basic support to +work you camera must support accessing a JPEG snapshot via a URL and you will +need to specify the "still_image_url" parameter which should be the location of +the JPEG image. As part of the basic support the following features will be provided: -MJPEG video streaming -Saving a snapshot -Recording(JPEG frame capture) -NOTE: for the basic support to work you camera must support accessing a JPEG -snapshot via a URL and you will need to specify the "still_image_url" parameter -which should be the location of the JPEG snapshot relative to you specified -base_url. For example "snapshot.cgi" or "image.jpg". - -To use this component you will need to add something like the following to your -config/configuration.yaml +To use this component, add the following to your config/configuration.yaml: camera: platform: generic - base_url: http://YOUR_CAMERA_IP_AND_PORT/ name: Door Camera - brand: dlink - family: DCS - model: DCS-930L username: YOUR_USERNAME password: YOUR_PASSWORD - still_image_url: image.jpg + still_image_url: http://YOUR_CAMERA_IP_AND_PORT/image.jpg VARIABLES: These are the variables for the device_data array: -base_url +still_image_url *Required -The base URL for accessing you camera +The URL your camera serves the image on. Example: http://192.168.1.21:2112/ name *Optional This parameter allows you to override the name of your camera in homeassistant - -brand -*Optional -The manufacturer of your device, used to help load the specific camera -functionality. - -family -*Optional -The family of devices by the specified brand, useful when many models -support the same settings. This used when attempting load up specific -device functionality. - -model -*Optional -The specific model number of your device. - -still_image_url -*Optional -Useful if using an unsupported camera model. This should point to the location -of the still image on your particular camera and should be relative to your -specified base_url. -Example: cam/image.jpg - username -*Required +*Optional THe username for acessing your camera password -*Required +*Optional the password for accessing your camera @@ -88,97 +57,39 @@ _LOGGER = logging.getLogger(__name__) # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): - """ Find and return Vera lights. """ - if not validate_config( - {DOMAIN: config}, - {DOMAIN: ['base_url', CONF_USERNAME, CONF_PASSWORD]}, - _LOGGER): + """ Adds a generic IP Camera. """ + if not validate_config({DOMAIN: config}, {DOMAIN: ['still_image_url']}, _LOGGER): return None - camera = GenericCamera(hass, config) - cameras = [camera] - - add_devices_callback(cameras) + add_devices_callback([GenericCamera(config)]) # pylint: disable=too-many-instance-attributes class GenericCamera(Camera): """ - Base class for cameras. - This is quite a large class but the camera component encompasses a lot of - functionality. It should take care of most of the heavy lifting and - plumbing associated with adding support for additional models of camera. - If you are adding support for a new camera your entity class should inherit - from this. + A generic implementation of an IP camera that is reachable over a URL. """ - def __init__(self, hass, device_info): - self.hass = hass - self._device_info = device_info - self._base_url = device_info.get('base_url') - if not self._base_url.endswith('/'): - self._base_url = self._base_url + '/' + def __init__(self, device_info): + super().__init__() + self._name = device_info.get('name', 'Generic Camera') self._username = device_info.get('username') self._password = device_info.get('password') - self._is_streaming = False - self._still_image_url = device_info.get('still_image_url', 'image.jpg') - self._logger = logging.getLogger(__name__) - def get_camera_image(self): + self._still_image_url += device_info.get('still_image_url', 'image.jpg') + + def camera_image(self): """ Return a still image reponse from the camera """ - if self.username and self.password: + if self._username and self._password: response = requests.get( - self.still_image_url, - auth=HTTPBasicAuth( - self.username, - self.password)) + self._still_image_url, + auth=HTTPBasicAuth(self._username,self._password)) else: - response = requests.get(self.still_image_url) + response = requests.get(self._still_image_url) return response.content - @property - def device_info(self): - """ Return the config data for this device """ - return self._device_info - @property def name(self): """ Return the name of this device """ - return self._device_info.get('name') or super().name - - @property - def state_attributes(self): - """ Returns optional state attributes. """ - attr = super().state_attributes - - return attr - - @property - def base_url(self): - """ Return the URL of the IP Camera """ - return self._base_url - - @property - def username(self): - """ Return the configured username """ - return self._username - - @property - def password(self): - """ Return the configured password """ - return self._password - - @property - def is_streaming(self): - return self._is_streaming - - @is_streaming.setter - # pylint: disable=arguments-differ - def is_streaming(self, value): - self._is_streaming = value - - @property - def still_image_url(self): - """ This should be implemented by different camera models. """ - return self.base_url + self._still_image_url + return self._name