From 81d29928b0ae87ce51a95c6b829c26a940eaea9e Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Thu, 14 Jun 2018 12:37:09 +0200 Subject: [PATCH] Pi Camera backend version 1.0, now properly controllable --- platypush/backend/camera/pi.py | 88 ++++++++++++++++++++++++++-------- platypush/plugins/camera/pi.py | 6 +-- setup.py | 5 +- 3 files changed, 73 insertions(+), 26 deletions(-) diff --git a/platypush/backend/camera/pi.py b/platypush/backend/camera/pi.py index 8a18f6f6..c2fc9346 100644 --- a/platypush/backend/camera/pi.py +++ b/platypush/backend/camera/pi.py @@ -1,13 +1,26 @@ +import json import socket import time import picamera -from threading import Event +from enum import Enum +from redis import Redis +from threading import Thread from platypush.backend import Backend class CameraPiBackend(Backend): + class CameraAction(Enum): + START_RECORDING = 'START_RECORDING' + STOP_RECORDING = 'STOP_RECORDING' + TAKE_PICTURE = 'TAKE_PICTURE' + + def __eq__(self, other): + return self.value == other + def __init__(self, listen_port, x_resolution=640, y_resolution=480, + redis_queue='platypush_mq_camera', + start_recording_on_startup=True, framerate=24, hflip=False, vflip=False, sharpness=0, contrast=0, brightness=50, video_stabilization=False, ISO=0, exposure_compensation=0, @@ -42,25 +55,63 @@ class CameraPiBackend(Backend): self.camera.color_effects = color_effects self.camera.rotation = rotation self.camera.crop = crop + self.start_recording_on_startup = start_recording_on_startup + self.redis = Redis() + self.redis_queue = redis_queue + self._recording_thread = None + + if self.start_recording_on_startup: + self.send_camera_action(self.CameraAction.START_RECORDING) self.logger.info('Initialized Pi camera backend') + def send_camera_action(self, action, **kwargs): + action = { + 'action': action.value, + **kwargs + } + + self.redis.rpush(self.redis_queue, json.dumps(action)) + def take_picture(self, image_file): self.logger.info('Capturing camera snapshot to {}'.format(image_file)) self.camera.capture(image_file) self.logger.info('Captured camera snapshot to {}'.format(image_file)) def start_recording(self, video_file=None, format='h264'): + def recording_thread(): + if video_file: + self.camera.start_recording(videofile, format=format) + while True: + self.camera.wait_recording(60) + else: + connection = self.server_socket.accept()[0].makefile('wb') + self.logger.info('Accepted client connection on port {}'. + format(self.listen_port)) + + try: + self.camera.start_recording(connection, format=format) + while True: + self.camera.wait_recording(60) + except ConnectionError: + self.logger.info('Client closed connection') + try: + self.stop_recording() + connection.close() + except: + pass + + self.send_camera_action(self.CameraAction.START_RECORDING) + + self._recording_thread = None + + if self._recording_thread: + self._recording_thread.join() + self.logger.info('Starting camera recording') + self._recording_thread = Thread(target=recording_thread) + self._recording_thread.start() - if video_file: - self.camera.start_recording(videofile, format=format) - else: - connection = self.server_socket.accept()[0].makefile('wb') - self.logger.info('Accepted client connection on port {}'. - format(self.listen_port)) - - self.camera.start_recording(connection, format=format) def stop_recording(self): self.logger.info('Stopping camera recording') @@ -73,20 +124,15 @@ class CameraPiBackend(Backend): def run(self): super().run() - while True: - restart = True + while not self.should_stop(): + msg = json.loads(self.redis.blpop(self.redis_queue)[1].decode()) - try: + if msg.get('action') == self.CameraAction.START_RECORDING: self.start_recording() - while True: - self.camera.wait_recording(60) - except ConnectionError: - self.logger.info('Client closed connection') - finally: - try: - self.stop_recording() - except Exception as e: - self.logger.exception(e) + elif msg.get('action') == self.CameraAction.STOP_RECORDING: + self.stop_recording() + elif msg.get('action') == self.CameraAction.TAKE_PICTURE: + self.take_picture(image_file=msg.get('image_file')) # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/camera/pi.py b/platypush/plugins/camera/pi.py index 653baa8b..4fb65aa8 100644 --- a/platypush/plugins/camera/pi.py +++ b/platypush/plugins/camera/pi.py @@ -7,17 +7,17 @@ from platypush.plugins import Plugin class CameraPiPlugin(Plugin): def start_recording(self): camera = get_backend('camera.pi') - camera.start_recording() + camera.send_camera_action(camera.CameraAction.START_RECORDING) return Response(output={'status':'ok'}) def stop_recording(self): camera = get_backend('camera.pi') - camera.stop_recording() + camera.send_camera_action(camera.CameraAction.STOP_RECORDING) return Response(output={'status':'ok'}) def take_picture(self, image_file): camera = get_backend('camera.pi') - camera.take_picture(image_file) + camera.send_camera_action(camera.CameraAction.TAKE_PICTURE, image_file=image_file) return Response(output={'image_file':image_file}) diff --git a/setup.py b/setup.py index 641d1f2d..076a626f 100755 --- a/setup.py +++ b/setup.py @@ -60,11 +60,12 @@ setup( install_requires = [ 'pyyaml', 'requires', + 'redis', ], extras_require = { 'Support for Apache Kafka backend': ['kafka-python'], 'Support for Pushbullet backend': ['requests', 'websocket-client'], - 'Support for HTTP backend': ['flask','websockets','redis'], + 'Support for HTTP backend': ['flask','websockets'], 'Support for HTTP poll backend': ['frozendict'], 'Support for database plugin': ['sqlalchemy'], 'Support for RSS feeds': ['feedparser'], @@ -86,7 +87,7 @@ setup( 'Support for MCP3008 analog-to-digital converter plugin': ['adafruit-mcp3008'], 'Support for smart cards detection': ['pyscard'], 'Support for ICal calendars': ['icalendar', 'python-dateutil'], - # 'Support for Leap Motion backend': ['git+ssh://git@github.com:BlackLight/leap-sdk-python3.git', 'redis'], + # 'Support for Leap Motion backend': ['git+ssh://git@github.com:BlackLight/leap-sdk-python3.git'], # 'Support for Flic buttons': ['git+ssh://git@github.com/50ButtonsEach/fliclib-linux-hci'] }, )