diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index fc5c739c888..9aefe4b3b66 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -33,8 +33,6 @@ SWITCH_ACTION_SNAPSHOT = 'snapshot' SERVICE_CAMERA = 'camera_service' -STATE_RECORDING = 'recording' - DEFAULT_RECORDING_SECONDS = 30 # Maps discovered services to their platforms @@ -46,6 +44,7 @@ DIR_DATETIME_FORMAT = '%Y-%m-%d_%H-%M-%S' REC_DIR_PREFIX = 'recording-' REC_IMG_PREFIX = 'recording_image-' +STATE_RECORDING = 'recording' STATE_STREAMING = 'streaming' STATE_IDLE = 'idle' @@ -121,33 +120,7 @@ def setup(hass, config): try: camera.is_streaming = True camera.update_ha_state() - - handler.request.sendall(bytes('HTTP/1.1 200 OK\r\n', 'utf-8')) - handler.request.sendall(bytes( - 'Content-type: multipart/x-mixed-replace; \ - boundary=--jpgboundary\r\n\r\n', 'utf-8')) - handler.request.sendall(bytes('--jpgboundary\r\n', 'utf-8')) - - # MJPEG_START_HEADER.format() - - while True: - img_bytes = camera.camera_image() - if img_bytes is None: - continue - headers_str = '\r\n'.join(( - 'Content-length: {}'.format(len(img_bytes)), - 'Content-type: image/jpeg', - )) + '\r\n\r\n' - - handler.request.sendall( - bytes(headers_str, 'utf-8') + - img_bytes + - bytes('\r\n', 'utf-8')) - - handler.request.sendall( - bytes('--jpgboundary\r\n', 'utf-8')) - - time.sleep(0.5) + camera.mjpeg_stream(handler) except (requests.RequestException, IOError): camera.is_streaming = False @@ -190,6 +163,34 @@ class Camera(Entity): """ Return bytes of camera image. """ raise NotImplementedError() + def mjpeg_stream(self, handler): + """ Generate an HTTP MJPEG stream from camera images. """ + handler.request.sendall(bytes('HTTP/1.1 200 OK\r\n', 'utf-8')) + handler.request.sendall(bytes( + 'Content-type: multipart/x-mixed-replace; \ + boundary=--jpgboundary\r\n\r\n', 'utf-8')) + handler.request.sendall(bytes('--jpgboundary\r\n', 'utf-8')) + + # MJPEG_START_HEADER.format() + while True: + img_bytes = self.camera_image() + if img_bytes is None: + continue + headers_str = '\r\n'.join(( + 'Content-length: {}'.format(len(img_bytes)), + 'Content-type: image/jpeg', + )) + '\r\n\r\n' + + handler.request.sendall( + bytes(headers_str, 'utf-8') + + img_bytes + + bytes('\r\n', 'utf-8')) + + handler.request.sendall( + bytes('--jpgboundary\r\n', 'utf-8')) + + time.sleep(0.5) + @property def state(self): """ Returns the state of the entity. """ diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index 0d59c8d60c7..7bbaa4846b5 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -14,6 +14,9 @@ from requests.auth import HTTPBasicAuth from homeassistant.helpers import validate_config from homeassistant.components.camera import DOMAIN, Camera +from homeassistant.const import HTTP_OK + +CONTENT_TYPE_HEADER = 'Content-Type' _LOGGER = logging.getLogger(__name__) @@ -41,6 +44,17 @@ class MjpegCamera(Camera): self._password = device_info.get('password') self._mjpeg_url = device_info['mjpeg_url'] + def camera_stream(self): + """ Return a mjpeg stream image response directly from the camera. """ + if self._username and self._password: + return requests.get(self._mjpeg_url, + auth=HTTPBasicAuth(self._username, + self._password), + stream=True) + else: + return requests.get(self._mjpeg_url, + stream=True) + def camera_image(self): """ Return a still image response from the camera. """ @@ -55,16 +69,22 @@ class MjpegCamera(Camera): jpg = data[jpg_start:jpg_end + 2] return jpg - if self._username and self._password: - with closing(requests.get(self._mjpeg_url, - auth=HTTPBasicAuth(self._username, - self._password), - stream=True)) as response: - return process_response(response) - else: - with closing(requests.get(self._mjpeg_url, - stream=True)) as response: - return process_response(response) + with closing(self.camera_stream()) as response: + return process_response(response) + + def mjpeg_stream(self, handler): + """ Generate an HTTP MJPEG stream from the camera. """ + response = self.camera_stream() + content_type = response.headers[CONTENT_TYPE_HEADER] + + handler.send_response(HTTP_OK) + handler.send_header(CONTENT_TYPE_HEADER, content_type) + handler.end_headers() + + for chunk in response.iter_content(chunk_size=1024): + if not chunk: + break + handler.wfile.write(chunk) @property def name(self):