From 30bb2113248afb62bdfaa9751645a483a9bc4669 Mon Sep 17 00:00:00 2001 From: Athemis Date: Thu, 31 Jan 2019 16:25:34 +0100 Subject: [PATCH 01/12] first attempt to add generic raspimjpeg worker --- phytopi/camera/camera.py | 48 ++++++++++++++++++ phytopi/camera_daemon/camera_daemon.py | 0 .../{camera_daemon => camera_old}/__init__.py | 0 phytopi/{camera => camera_old}/base_camera.py | 0 phytopi/{camera => camera_old}/camera_pi.py | 0 phytopi/phytopi.db | Bin 98304 -> 0 bytes phytopi/routes.py | 5 +- requirements_pi.txt | 19 ------- 8 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 phytopi/camera/camera.py delete mode 100644 phytopi/camera_daemon/camera_daemon.py rename phytopi/{camera_daemon => camera_old}/__init__.py (100%) rename phytopi/{camera => camera_old}/base_camera.py (100%) rename phytopi/{camera => camera_old}/camera_pi.py (100%) delete mode 100644 phytopi/phytopi.db delete mode 100644 requirements_pi.txt diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py new file mode 100644 index 0000000..8608127 --- /dev/null +++ b/phytopi/camera/camera.py @@ -0,0 +1,48 @@ +import io +import os +import sys +import subprocess +import signal + +from phytopi import db, models, app + +class CameraWorker(object): + def __init__(self, pidfile='/tmp/raspimjpeg.pid', executable='/usr/local/bin/raspimjpeg', fifo='/dev/shm/mjpeg/mjpeg_fifo', app=None): + self.pidfile = pidfile + self.executable = executable + self.fifo = fifo + self.proc = None + self.pid = None + self.app = app + + def start(self): + # create FIFO if not existent + if not os.path.isfile(self.fifo): + os.mkfifo(self.fifo) + + # check if process is already running + if os.path.isfile(self.pidfile): + with open(self.pidfile) as fh: + self.pid = int(fh.read()) + + # if not, spawn new process and create pid file + self.proc = subprocess.Popen(self.executable, stdout=subprocess.PIPE) + self.pid = self.proc.pid + with open(self.pidfile) as fh: + fh.write(self.pid) + + def stop(self): + # if process was spawned by this instance, killing is easy + if self.proc: + self.proc.kill() + else: + # otherwise send SIGTERM to pid + os.kill(self.pid, signal.SIGTERM) + os.unlink(self.fifo) + + def send_cmd(self, cmd): + with open(self.fifo, 'w') as fh: + fh.write(cmd) + + def read_output(self): + return self.proc.stdout.readlines() \ No newline at end of file diff --git a/phytopi/camera_daemon/camera_daemon.py b/phytopi/camera_daemon/camera_daemon.py deleted file mode 100644 index e69de29..0000000 diff --git a/phytopi/camera_daemon/__init__.py b/phytopi/camera_old/__init__.py similarity index 100% rename from phytopi/camera_daemon/__init__.py rename to phytopi/camera_old/__init__.py diff --git a/phytopi/camera/base_camera.py b/phytopi/camera_old/base_camera.py similarity index 100% rename from phytopi/camera/base_camera.py rename to phytopi/camera_old/base_camera.py diff --git a/phytopi/camera/camera_pi.py b/phytopi/camera_old/camera_pi.py similarity index 100% rename from phytopi/camera/camera_pi.py rename to phytopi/camera_old/camera_pi.py diff --git a/phytopi/phytopi.db b/phytopi/phytopi.db deleted file mode 100644 index 0519c720bad40fa4d17de39a8783b093f68751b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98304 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®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 OHq|q+Ft#wa-~j;COe1Ij diff --git a/phytopi/routes.py b/phytopi/routes.py index 35e73b6..a85074f 100644 --- a/phytopi/routes.py +++ b/phytopi/routes.py @@ -1,6 +1,6 @@ from flask import render_template, Response, flash, jsonify, request, stream_with_context, send_file -from phytopi.camera.camera_pi import Camera +from phytopi.camera.camera import CameraWorker from phytopi.forms import CameraSettingsForm from phytopi import app @@ -10,7 +10,8 @@ from phytopi.models import Dataset from io import BytesIO import zipstream -camera = Camera(app=app) +camera = CameraWorker(app=app) +camera.start() # camera = app.camera save_frames = False diff --git a/requirements_pi.txt b/requirements_pi.txt deleted file mode 100644 index abee2bc..0000000 --- a/requirements_pi.txt +++ /dev/null @@ -1,19 +0,0 @@ -alembic>=1.0.1 -Click>=7.0 -Flask>=1.0.2 -Flask-Migrate>=2.3.0 -Flask-SQLAlchemy>=2.3.2 -Flask-WTF>=0.14.2 -itsdangerous>=1.1.0 -Jinja2>=2.10 -Mako>=1.0.7 -MarkupSafe>=1.0 -picamera>=1.13 -Pillow>=5.3.0 -python-dateutil>=2.7.5 -python-editor>=1.0.3 -six>=1.11.0 -SQLAlchemy>=1.2.12 -Werkzeug>=0.14.1 -WTForms>=2.2.1 -zipstream>=1.1.4 From e65ed89053f5a093bd860bcde05488d3bff9e08f Mon Sep 17 00:00:00 2001 From: Athemis Date: Thu, 31 Jan 2019 17:17:37 +0100 Subject: [PATCH 02/12] new camera worker functional in standalone mode --- phytopi/camera/camera.py | 52 +++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index 8608127..800d801 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -1,48 +1,62 @@ import io import os import sys +import stat import subprocess import signal +from enum import Enum from phytopi import db, models, app +# map commands +camera_cmds = { + 'start_timelapse': 'tl 1', + 'stop_timelapse': 'tl 0', +} + +class CameraStatus(Enum): + STOPPED = 0 + IDLE = 1 + TIMELAPSE = 2 + VIDEO = 3 + class CameraWorker(object): - def __init__(self, pidfile='/tmp/raspimjpeg.pid', executable='/usr/local/bin/raspimjpeg', fifo='/dev/shm/mjpeg/mjpeg_fifo', app=None): + def __init__(self, pidfile='/tmp/raspimjpeg.pid', executable='/usr/local/bin/raspimjpeg', fifo='/dev/shm/mjpeg/FIFO', app=None): self.pidfile = pidfile self.executable = executable self.fifo = fifo self.proc = None self.pid = None self.app = app + self.status = CameraStatus.STOPPED def start(self): - # create FIFO if not existent - if not os.path.isfile(self.fifo): - os.mkfifo(self.fifo) + # create FIFO + os.mkfifo(self.fifo) # check if process is already running if os.path.isfile(self.pidfile): - with open(self.pidfile) as fh: + with open(self.pidfile, 'r') as fh: self.pid = int(fh.read()) # if not, spawn new process and create pid file - self.proc = subprocess.Popen(self.executable, stdout=subprocess.PIPE) + self.proc = subprocess.Popen([self.executable]) self.pid = self.proc.pid - with open(self.pidfile) as fh: - fh.write(self.pid) + with open(self.pidfile, 'w') as fh: + fh.write(str(self.pid)) + self.status = CameraStatus.IDLE def stop(self): - # if process was spawned by this instance, killing is easy - if self.proc: - self.proc.kill() - else: - # otherwise send SIGTERM to pid - os.kill(self.pid, signal.SIGTERM) + os.killpg(os.getpgid(self.pid), signal.SIGTERM) os.unlink(self.fifo) + os.unlink(self.pidfile) + self.status = CameraStatus.STOPPED def send_cmd(self, cmd): - with open(self.fifo, 'w') as fh: - fh.write(cmd) - - def read_output(self): - return self.proc.stdout.readlines() \ No newline at end of file + if cmd in camera_cmds: + with open(self.fifo, 'w') as fh: + fh.write(cmd) + if cmd == camera_cmds['start_timelapse']: + self.status = CameraStatus.TIMELAPSE + if cmd == camera_cmds['stop_timelapse']: + self.status = CameraStatus.IDLE \ No newline at end of file From a8c7d8e74f25c38dcae964105619f3213dd62cb4 Mon Sep 17 00:00:00 2001 From: Athemis Date: Thu, 31 Jan 2019 17:31:38 +0100 Subject: [PATCH 03/12] fix creation of fifo --- phytopi/camera/camera.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index 800d801..2abd649 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -11,7 +11,7 @@ from phytopi import db, models, app # map commands camera_cmds = { 'start_timelapse': 'tl 1', - 'stop_timelapse': 'tl 0', + 'stop_timelapse': 'tl 0' } class CameraStatus(Enum): @@ -31,13 +31,18 @@ class CameraWorker(object): self.status = CameraStatus.STOPPED def start(self): - # create FIFO - os.mkfifo(self.fifo) - # check if process is already running if os.path.isfile(self.pidfile): with open(self.pidfile, 'r') as fh: self.pid = int(fh.read()) + self.status = CameraStatus.IDLE + return + + # create FIFO, if not existent + try: + os.mkfifo(self.fifo) + except FileExistsError: + pass # if not, spawn new process and create pid file self.proc = subprocess.Popen([self.executable]) From 7558c6d196cc6ad76de517d2c611c9644175c129 Mon Sep 17 00:00:00 2001 From: Athemis Date: Fri, 1 Feb 2019 11:27:28 +0100 Subject: [PATCH 04/12] fix killing of child processes without killing the whole app --- phytopi/camera/camera.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index 2abd649..dc159a4 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -8,17 +8,15 @@ import signal from enum import Enum from phytopi import db, models, app -# map commands -camera_cmds = { - 'start_timelapse': 'tl 1', - 'stop_timelapse': 'tl 0' -} +class CameraActionError(Exception): + pass class CameraStatus(Enum): STOPPED = 0 IDLE = 1 TIMELAPSE = 2 VIDEO = 3 + SHOT = 4 class CameraWorker(object): def __init__(self, pidfile='/tmp/raspimjpeg.pid', executable='/usr/local/bin/raspimjpeg', fifo='/dev/shm/mjpeg/FIFO', app=None): @@ -45,7 +43,7 @@ class CameraWorker(object): pass # if not, spawn new process and create pid file - self.proc = subprocess.Popen([self.executable]) + self.proc = subprocess.Popen([self.executable], preexec_fn=os.setsid) self.pid = self.proc.pid with open(self.pidfile, 'w') as fh: fh.write(str(self.pid)) @@ -58,10 +56,16 @@ class CameraWorker(object): self.status = CameraStatus.STOPPED def send_cmd(self, cmd): - if cmd in camera_cmds: - with open(self.fifo, 'w') as fh: - fh.write(cmd) - if cmd == camera_cmds['start_timelapse']: - self.status = CameraStatus.TIMELAPSE - if cmd == camera_cmds['stop_timelapse']: - self.status = CameraStatus.IDLE \ No newline at end of file + with open(self.fifo, 'w') as fh: + fh.write(cmd) + + def timelapse(self, interval = 0): + if self.status == CameraStatus.IDLE: + self.send_cmd('tl {}'.format(str(interval))) + self.status = CameraStatus.TIMELAPSE + elif self.status == CameraStatus.TIMELAPSE: + self.send_cmd('tl 0') + self.status == CameraStatus.IDLE + else: + raise CameraActionError('Camera neither idle nor in timelapse mode!') + \ No newline at end of file From c13cb3355ea42ff3ac6fed2872a90d879562ab68 Mon Sep 17 00:00:00 2001 From: Athemis Date: Fri, 1 Feb 2019 12:52:02 +0100 Subject: [PATCH 05/12] implement get_image in new camera module --- phytopi/camera/camera.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index dc159a4..3c84759 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -6,6 +6,7 @@ import subprocess import signal from enum import Enum +from PIL import Image from phytopi import db, models, app class CameraActionError(Exception): @@ -68,4 +69,9 @@ class CameraWorker(object): self.status == CameraStatus.IDLE else: raise CameraActionError('Camera neither idle nor in timelapse mode!') + + def get_frame(self, thumbnail=False): + img = Image.open('/dev/shm/mjpeg/cam.jpg') + return img + \ No newline at end of file From 416f6ec2a6370adc6529792651bc304d1b632893 Mon Sep 17 00:00:00 2001 From: Athemis Date: Fri, 1 Feb 2019 13:18:13 +0100 Subject: [PATCH 06/12] reimplement start/stop of timelapse from gui --- phytopi/camera/camera.py | 15 ++++++++---- phytopi/routes.py | 49 +++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index 3c84759..24aef09 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -60,18 +60,25 @@ class CameraWorker(object): with open(self.fifo, 'w') as fh: fh.write(cmd) - def timelapse(self, interval = 0): + def start_timelapse(self, interval = 0): if self.status == CameraStatus.IDLE: self.send_cmd('tl {}'.format(str(interval))) self.status = CameraStatus.TIMELAPSE - elif self.status == CameraStatus.TIMELAPSE: + else: + raise CameraActionError('Camera not idle!') + + def stop_timelapse(self): + if self.status == CameraStatus.TIMELAPSE: self.send_cmd('tl 0') self.status == CameraStatus.IDLE else: - raise CameraActionError('Camera neither idle nor in timelapse mode!') + raise CameraActionError('Camera not in timelapse mode!') def get_frame(self, thumbnail=False): + output = io.BytesIO() img = Image.open('/dev/shm/mjpeg/cam.jpg') - return img + img.save(output, format='JPEG') + output.seek(0, 0) + return output.getvalue() \ No newline at end of file diff --git a/phytopi/routes.py b/phytopi/routes.py index a85074f..d2818fc 100644 --- a/phytopi/routes.py +++ b/phytopi/routes.py @@ -1,6 +1,6 @@ from flask import render_template, Response, flash, jsonify, request, stream_with_context, send_file -from phytopi.camera.camera import CameraWorker +from phytopi.camera.camera import CameraWorker, CameraStatus from phytopi.forms import CameraSettingsForm from phytopi import app @@ -12,7 +12,6 @@ import zipstream camera = CameraWorker(app=app) camera.start() -# camera = app.camera save_frames = False @app.route('/') @@ -23,34 +22,38 @@ def index(): @app.route('/toggle_timelapse', methods=['GET', 'POST']) def start_stop_timelapse(): - global save_frames + # global save_frames global current_dataset global camera - if save_frames: - save_frames = False - timelapse_interval = None - btn_text = "Start" - btn_class = 'btn-primary' - camera.current_dataset = None - camera.last_saved = None - print(" > switched off timelapse mode") - else: - save_frames = True - timelapse_interval = 1200 - btn_text = "Stop" - btn_class = 'btn-danger' - dataset = Dataset() - db.session.add(dataset) - db.session.commit() - camera.current_dataset = dataset.id - print(" > switched on timelapse mode") + # if save_frames: + # save_frames = False + # timelapse_interval = None + # btn_text = "Start" + # btn_class = 'btn-primary' + # camera.current_dataset = None + # camera.last_saved = None + # print(" > switched off timelapse mode") + # else: + # save_frames = True + # timelapse_interval = 1200 + # btn_text = "Stop" + # btn_class = 'btn-danger' + # dataset = Dataset() + # db.session.add(dataset) + # db.session.commit() + # camera.current_dataset = dataset.id + # print(" > switched on timelapse mode") + + # camera.set_timelapse_interval(timelapse_interval) + if camera.status == CameraStatus.IDLE: + camera.start_timelapse(1200) + elif camera.status == CameraStatus.TIMELAPSE: + camera.stop_timelapse() - camera.set_timelapse_interval(timelapse_interval) return jsonify(btn_text=btn_text, btn_class=btn_class) def gen(camera): -# def gen(): """Video streaming generator function.""" while True: frame = camera.get_frame(thumbnail=True) From fb7f77b3a841da333862fce9d5da23e7e6af5263 Mon Sep 17 00:00:00 2001 From: Athemis Date: Fri, 1 Feb 2019 14:50:08 +0100 Subject: [PATCH 07/12] fix setting timelapse interval --- phytopi/camera/camera.py | 5 +++-- phytopi/routes.py | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index 24aef09..d9bfb89 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -60,9 +60,10 @@ class CameraWorker(object): with open(self.fifo, 'w') as fh: fh.write(cmd) - def start_timelapse(self, interval = 0): + def start_timelapse(self, interval=300): if self.status == CameraStatus.IDLE: - self.send_cmd('tl {}'.format(str(interval))) + self.send_cmd('tv {}'.format(str(interval))) + self.send_cmd('tl 1') self.status = CameraStatus.TIMELAPSE else: raise CameraActionError('Camera not idle!') diff --git a/phytopi/routes.py b/phytopi/routes.py index d2818fc..dbb4a3d 100644 --- a/phytopi/routes.py +++ b/phytopi/routes.py @@ -46,8 +46,12 @@ def start_stop_timelapse(): # camera.set_timelapse_interval(timelapse_interval) if camera.status == CameraStatus.IDLE: - camera.start_timelapse(1200) + btn_text = "Stop" + btn_class = 'btn-danger' + camera.start_timelapse() elif camera.status == CameraStatus.TIMELAPSE: + btn_text = "Start" + btn_class = 'btn-primary' camera.stop_timelapse() return jsonify(btn_text=btn_text, btn_class=btn_class) From 20e1d696a0ea013e1893ed2d5364c1b4c5d86e6d Mon Sep 17 00:00:00 2001 From: Athemis Date: Fri, 1 Feb 2019 14:52:53 +0100 Subject: [PATCH 08/12] send all timelapse commands at once --- phytopi/camera/camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index d9bfb89..47db3b1 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -62,7 +62,7 @@ class CameraWorker(object): def start_timelapse(self, interval=300): if self.status == CameraStatus.IDLE: - self.send_cmd('tv {}'.format(str(interval))) + self.send_cmd('tv {} \n tl 1'.format(str(interval))) self.send_cmd('tl 1') self.status = CameraStatus.TIMELAPSE else: From cd2484a8d833b00f8c00b5091ce12cd056c0ecda Mon Sep 17 00:00:00 2001 From: Athemis Date: Fri, 1 Feb 2019 14:56:18 +0100 Subject: [PATCH 09/12] end command with newline char --- phytopi/camera/camera.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index 47db3b1..d89012e 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -58,11 +58,11 @@ class CameraWorker(object): def send_cmd(self, cmd): with open(self.fifo, 'w') as fh: - fh.write(cmd) + fh.write('{}\n'.format(cmd)) def start_timelapse(self, interval=300): if self.status == CameraStatus.IDLE: - self.send_cmd('tv {} \n tl 1'.format(str(interval))) + self.send_cmd('tv {}'.format(str(interval))) self.send_cmd('tl 1') self.status = CameraStatus.TIMELAPSE else: From 6ae1f9f22fb7e26d30c1c60dfddbeb33cdc196ad Mon Sep 17 00:00:00 2001 From: Athemis Date: Fri, 1 Feb 2019 16:56:41 +0100 Subject: [PATCH 10/12] Restore timelapse button status on reload --- phytopi/camera/camera.py | 19 ++++++++++++------- phytopi/routes.py | 9 +++++++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index d89012e..41c146d 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -5,6 +5,7 @@ import stat import subprocess import signal +from time import sleep from enum import Enum from PIL import Image from phytopi import db, models, app @@ -56,21 +57,27 @@ class CameraWorker(object): os.unlink(self.pidfile) self.status = CameraStatus.STOPPED - def send_cmd(self, cmd): + def _send_cmd(self, cmd): with open(self.fifo, 'w') as fh: fh.write('{}\n'.format(cmd)) + def _send_cmds(self, cmds): + for cmd in cmds: + self._send_cmd(cmd) + # don't flood the fifo + sleep(1) + def start_timelapse(self, interval=300): if self.status == CameraStatus.IDLE: - self.send_cmd('tv {}'.format(str(interval))) - self.send_cmd('tl 1') + cmds = ['tv {}'.format(interval), 'tl 1'] + self._send_cmds(cmds) self.status = CameraStatus.TIMELAPSE else: raise CameraActionError('Camera not idle!') def stop_timelapse(self): if self.status == CameraStatus.TIMELAPSE: - self.send_cmd('tl 0') + self._send_cmd('tl 0') self.status == CameraStatus.IDLE else: raise CameraActionError('Camera not in timelapse mode!') @@ -80,6 +87,4 @@ class CameraWorker(object): img = Image.open('/dev/shm/mjpeg/cam.jpg') img.save(output, format='JPEG') output.seek(0, 0) - return output.getvalue() - - \ No newline at end of file + return output.getvalue() \ No newline at end of file diff --git a/phytopi/routes.py b/phytopi/routes.py index dbb4a3d..a8a6010 100644 --- a/phytopi/routes.py +++ b/phytopi/routes.py @@ -12,12 +12,17 @@ import zipstream camera = CameraWorker(app=app) camera.start() -save_frames = False + @app.route('/') @app.route('/index') def index(): - content = {'timelapse': save_frames} + global camera + if camera.status == CameraStatus.TIMELAPSE: + timelapse = True + else: + timelapse = False + content = {'timelapse': timelapse} return render_template('index.html', content=content) @app.route('/toggle_timelapse', methods=['GET', 'POST']) From 8830e17f2f4c6031da0c6ceeb81544d4adaca2e6 Mon Sep 17 00:00:00 2001 From: Athemis Date: Fri, 1 Feb 2019 17:01:41 +0100 Subject: [PATCH 11/12] fix setting of correct camera status on timelapse stop --- phytopi/camera/camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index 41c146d..bfac410 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -78,7 +78,7 @@ class CameraWorker(object): def stop_timelapse(self): if self.status == CameraStatus.TIMELAPSE: self._send_cmd('tl 0') - self.status == CameraStatus.IDLE + self.status = CameraStatus.IDLE else: raise CameraActionError('Camera not in timelapse mode!') From 173f1ca5d1578cb3015814f600e0b666082f2cc5 Mon Sep 17 00:00:00 2001 From: Athemis Date: Mon, 11 Feb 2019 13:37:26 +0100 Subject: [PATCH 12/12] refactor application structure --- phytopi/__init__.py | 8 ++++++-- phytopi/camera/__init__.py | 5 +++++ phytopi/camera/camera.py | 6 +++--- phytopi/errors/__init__.py | 5 +++++ phytopi/errors/handlers.py | 0 phytopi/models.py | 11 ++++++----- 6 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 phytopi/errors/__init__.py create mode 100644 phytopi/errors/handlers.py diff --git a/phytopi/__init__.py b/phytopi/__init__.py index ee384c0..51df99e 100644 --- a/phytopi/__init__.py +++ b/phytopi/__init__.py @@ -3,7 +3,6 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from phytopi.config import Config -# from phytopi.camera.camera_pi import Camera app = Flask(__name__, instance_relative_config=True) @@ -11,6 +10,11 @@ app.config.from_object(Config) db = SQLAlchemy(app) migrate = Migrate(app, db) -# camera = Camera(app=app) + +from phytopi.errors import bp as errors_bp +from phytopi.camera import bp as camera_bp + +app.register_blueprint(errors_bp) +app.register_blueprint(camera_bp) from phytopi import routes, models diff --git a/phytopi/camera/__init__.py b/phytopi/camera/__init__.py index e69de29..9614332 100644 --- a/phytopi/camera/__init__.py +++ b/phytopi/camera/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('camera', __name__) + +from phytopi.camera import camera \ No newline at end of file diff --git a/phytopi/camera/camera.py b/phytopi/camera/camera.py index bfac410..2e001f6 100644 --- a/phytopi/camera/camera.py +++ b/phytopi/camera/camera.py @@ -8,7 +8,8 @@ import signal from time import sleep from enum import Enum from PIL import Image -from phytopi import db, models, app +from phytopi import db, models +from phytopi.models import CameraSettings class CameraActionError(Exception): pass @@ -21,13 +22,12 @@ class CameraStatus(Enum): SHOT = 4 class CameraWorker(object): - def __init__(self, pidfile='/tmp/raspimjpeg.pid', executable='/usr/local/bin/raspimjpeg', fifo='/dev/shm/mjpeg/FIFO', app=None): + def __init__(self, pidfile='/tmp/raspimjpeg.pid', executable='/usr/local/bin/raspimjpeg', fifo='/dev/shm/mjpeg/FIFO'): self.pidfile = pidfile self.executable = executable self.fifo = fifo self.proc = None self.pid = None - self.app = app self.status = CameraStatus.STOPPED def start(self): diff --git a/phytopi/errors/__init__.py b/phytopi/errors/__init__.py new file mode 100644 index 0000000..5d1beb0 --- /dev/null +++ b/phytopi/errors/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('errors', __name__) + +from phytopi.errors import handlers \ No newline at end of file diff --git a/phytopi/errors/handlers.py b/phytopi/errors/handlers.py new file mode 100644 index 0000000..e69de29 diff --git a/phytopi/models.py b/phytopi/models.py index b49e45f..828ddc2 100644 --- a/phytopi/models.py +++ b/phytopi/models.py @@ -1,6 +1,8 @@ from datetime import datetime from phytopi import db + + class Dataset(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(64), index=True) @@ -29,17 +31,16 @@ class CameraSettings(db.Model): fps = db.Column(db.Integer, default=5) width = db.Column(db.Integer, default=3280) height = db.Column(db.Integer, default=2464) - fix_shutter = db.Column(db.Boolean, default=False) - fix_wb = db.Column(db.Boolean, default=False) + exposure_mode = db.Column(db.String(64), default='auto') - def __init__(self, name, iso=100, fps=5, width=3280, height=2464, fix_shutter=False, fix_wb=False): + def __init__(self, name, iso=100, fps=5, width=3280, height=2464, exposure_mode='auto'): self.name = name self.iso = iso self.fps = fps self.width = width self.height = height - self.fix_shutter = fix_shutter - self.fix_wb = fix_wb + self.exposure_mode = exposure_mode + def __repr__(self): return ''.format(self.name) \ No newline at end of file