-import aiofiles
import aiofiles.os
import asyncio
import collections
+import concurrent.futures
import datetime
+import functools
import io
import os
-import picamera
+import picamera # type: ignore
+import PIL # type: ignore
from controlpi import BasePlugin, Message, MessageTemplate
+from typing import Deque, Tuple
+
class Camera(BasePlugin):
CONF_SCHEMA = {'properties':
{'pause': {'type': 'number'},
'keep': {'type': 'integer'},
'path': {'type': 'string'},
- 'resolution': {'type': 'string'},
- 'resize': {'type': 'string'},
- 'iso': {'type': 'integer'}},
+ 'iso': {'type': 'integer'},
+ 'full': {'type': 'array', 'items': {'type': 'integer'},
+ 'minItems': 2, 'maxItems': 2},
+ 'web': {'type': 'array', 'items': {'type': 'integer'},
+ 'minItems': 2, 'maxItems': 2},
+ 'thumb': {'type': 'array', 'items': {'type': 'integer'},
+ 'minItems': 2, 'maxItems': 2}},
'required': ['pause', 'keep', 'path']}
async def _receive(self, message: Message) -> None:
def process_conf(self) -> None:
"""Register plugin as bus client."""
- self._images = collections.deque()
+ self._images: Deque[Tuple[str, str]] = collections.deque()
self._capture = False
- self._resolution = '1024x768'
- if 'resolution' in self.conf:
- self._resolution = self.conf['resolution']
- self._resize = '640x480'
- if 'resize' in self.conf:
- self._resize = self.conf['resize']
self._iso = 200
if 'iso' in self.conf:
self._iso = self.conf['iso']
+ self._full = (1024, 768)
+ if 'full' in self.conf:
+ self._full = self.conf['full']
+ self._web = (640, 480)
+ if 'web' in self.conf:
+ self._web = self.conf['web']
+ self._thumb = (160, 120)
+ if 'thumb' in self.conf:
+ self._thumb = self.conf['thumb']
sends = [MessageTemplate({'event': {'const': 'new image'},
'image': {'type': 'string'},
'date': {'type': 'string'}}),
async def run(self) -> None:
"""Run camera."""
+ loop = asyncio.get_running_loop()
+ executor = concurrent.futures.ThreadPoolExecutor()
camera = picamera.PiCamera()
- camera.resolution = self._resolution
+ camera.resolution = (2592, 1944) # Maximal resolution of PiCamera
+ camera.framerate = 15 # Maximal framerate at maximal resolution
camera.iso = self._iso
+ web_path = os.path.join(self.conf['path'], 'web.jpg')
+ thumb_path = os.path.join(self.conf['path'], 'thumb.jpg')
try:
while True:
if self._capture:
filepath = os.path.join(self.conf['path'],
filename)
await aiofiles.os.remove(filepath)
+ timestamp = datetime.datetime.now()
+ iso_time = timestamp.strftime('%Y-%m-%d %H:%M:%S')
+ full_name = timestamp.strftime('%Y%m%d%H%M%S%f')+'.jpg'
+ full_path = os.path.join(self.conf['path'], full_name)
stream = io.BytesIO()
- camera.capture(stream, 'jpeg', resize=self._resize)
- timestamp = datetime.datetime.utcnow()
- filename = timestamp.strftime('%Y%m%d%H%M%S%f') + '.jpg'
- filedatetime = timestamp.strftime('%Y-%m-%d %H:%M:%S')
- filepath = os.path.join(self.conf['path'], filename)
- async with aiofiles.open(filepath, 'wb') as f:
- await f.write(stream.getvalue())
+ await loop.run_in_executor(
+ executor,
+ functools.partial(
+ camera.capture, stream, 'jpeg'))
+ stream.seek(0)
+ raw_image = await loop.run_in_executor(
+ executor,
+ functools.partial(PIL.Image.open, stream))
stream.close()
- self._images.append((filename, filedatetime))
+ full_image = await loop.run_in_executor(
+ executor,
+ functools.partial(
+ raw_image.resize,
+ self._full,
+ PIL.Image.ANTIALIAS))
+ await loop.run_in_executor(
+ executor,
+ functools.partial(
+ full_image.save, full_path))
+ web_image = await loop.run_in_executor(
+ executor,
+ functools.partial(
+ raw_image.resize,
+ self._web,
+ PIL.Image.ANTIALIAS))
+ await loop.run_in_executor(
+ executor,
+ functools.partial(
+ web_image.save, web_path))
+ thumb_image = await loop.run_in_executor(
+ executor,
+ functools.partial(
+ raw_image.resize,
+ self._thumb,
+ PIL.Image.ANTIALIAS))
+ await loop.run_in_executor(
+ executor,
+ functools.partial(
+ thumb_image.save, thumb_path))
+ self._images.append((full_name, iso_time))
await self.bus.send(Message(self.name,
{'event': 'new image',
- 'image': filename,
- 'date': filedatetime}))
+ 'image': full_name,
+ 'date': iso_time}))
+ await asyncio.sleep(self.conf['pause'])
camera.stop_preview()
await asyncio.sleep(2)
except asyncio.CancelledError:
camera.stop_preview()
camera.close()
while len(self._images) > 0:
- filename = self._images.popleft()[0]
- filepath = os.path.join(self.conf['path'],
- filename)
- os.remove(filepath)
+ file_name = self._images.popleft()[0]
+ file_path = os.path.join(self.conf['path'],
+ filename)
+ os.remove(file_path)
+ if os.path.isfile(web_path):
+ os.remove(web_path)
+ if os.path.isfile(thumb_path):
+ os.remove(thumb_path)
+ executor.shutdown(True)
raise