Pi Camera backend version 1.0, now properly controllable

This commit is contained in:
Fabio Manganiello 2018-06-14 12:37:09 +02:00
parent 2b73f71803
commit 81d29928b0
3 changed files with 73 additions and 26 deletions

View file

@ -1,13 +1,26 @@
import json
import socket import socket
import time import time
import picamera import picamera
from threading import Event from enum import Enum
from redis import Redis
from threading import Thread
from platypush.backend import Backend from platypush.backend import Backend
class CameraPiBackend(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, 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, framerate=24, hflip=False, vflip=False,
sharpness=0, contrast=0, brightness=50, sharpness=0, contrast=0, brightness=50,
video_stabilization=False, ISO=0, exposure_compensation=0, video_stabilization=False, ISO=0, exposure_compensation=0,
@ -42,25 +55,63 @@ class CameraPiBackend(Backend):
self.camera.color_effects = color_effects self.camera.color_effects = color_effects
self.camera.rotation = rotation self.camera.rotation = rotation
self.camera.crop = crop 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') 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): def take_picture(self, image_file):
self.logger.info('Capturing camera snapshot to {}'.format(image_file)) self.logger.info('Capturing camera snapshot to {}'.format(image_file))
self.camera.capture(image_file) self.camera.capture(image_file)
self.logger.info('Captured camera snapshot to {}'.format(image_file)) self.logger.info('Captured camera snapshot to {}'.format(image_file))
def start_recording(self, video_file=None, format='h264'): def start_recording(self, video_file=None, format='h264'):
self.logger.info('Starting camera recording') def recording_thread():
if video_file: if video_file:
self.camera.start_recording(videofile, format=format) self.camera.start_recording(videofile, format=format)
while True:
self.camera.wait_recording(60)
else: else:
connection = self.server_socket.accept()[0].makefile('wb') connection = self.server_socket.accept()[0].makefile('wb')
self.logger.info('Accepted client connection on port {}'. self.logger.info('Accepted client connection on port {}'.
format(self.listen_port)) format(self.listen_port))
try:
self.camera.start_recording(connection, format=format) 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()
def stop_recording(self): def stop_recording(self):
self.logger.info('Stopping camera recording') self.logger.info('Stopping camera recording')
@ -73,20 +124,15 @@ class CameraPiBackend(Backend):
def run(self): def run(self):
super().run() super().run()
while True: while not self.should_stop():
restart = True msg = json.loads(self.redis.blpop(self.redis_queue)[1].decode())
try: if msg.get('action') == self.CameraAction.START_RECORDING:
self.start_recording() self.start_recording()
while True: elif msg.get('action') == self.CameraAction.STOP_RECORDING:
self.camera.wait_recording(60)
except ConnectionError:
self.logger.info('Client closed connection')
finally:
try:
self.stop_recording() self.stop_recording()
except Exception as e: elif msg.get('action') == self.CameraAction.TAKE_PICTURE:
self.logger.exception(e) self.take_picture(image_file=msg.get('image_file'))
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View file

@ -7,17 +7,17 @@ from platypush.plugins import Plugin
class CameraPiPlugin(Plugin): class CameraPiPlugin(Plugin):
def start_recording(self): def start_recording(self):
camera = get_backend('camera.pi') camera = get_backend('camera.pi')
camera.start_recording() camera.send_camera_action(camera.CameraAction.START_RECORDING)
return Response(output={'status':'ok'}) return Response(output={'status':'ok'})
def stop_recording(self): def stop_recording(self):
camera = get_backend('camera.pi') camera = get_backend('camera.pi')
camera.stop_recording() camera.send_camera_action(camera.CameraAction.STOP_RECORDING)
return Response(output={'status':'ok'}) return Response(output={'status':'ok'})
def take_picture(self, image_file): def take_picture(self, image_file):
camera = get_backend('camera.pi') 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}) return Response(output={'image_file':image_file})

View file

@ -60,11 +60,12 @@ setup(
install_requires = [ install_requires = [
'pyyaml', 'pyyaml',
'requires', 'requires',
'redis',
], ],
extras_require = { extras_require = {
'Support for Apache Kafka backend': ['kafka-python'], 'Support for Apache Kafka backend': ['kafka-python'],
'Support for Pushbullet backend': ['requests', 'websocket-client'], '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 HTTP poll backend': ['frozendict'],
'Support for database plugin': ['sqlalchemy'], 'Support for database plugin': ['sqlalchemy'],
'Support for RSS feeds': ['feedparser'], 'Support for RSS feeds': ['feedparser'],
@ -86,7 +87,7 @@ setup(
'Support for MCP3008 analog-to-digital converter plugin': ['adafruit-mcp3008'], 'Support for MCP3008 analog-to-digital converter plugin': ['adafruit-mcp3008'],
'Support for smart cards detection': ['pyscard'], 'Support for smart cards detection': ['pyscard'],
'Support for ICal calendars': ['icalendar', 'python-dateutil'], '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'] # 'Support for Flic buttons': ['git+ssh://git@github.com/50ButtonsEach/fliclib-linux-hci']
}, },
) )