Compare commits

..

No commits in common. "raspimjpeg" and "master" have entirely different histories.

12 changed files with 50 additions and 149 deletions

View file

@ -3,6 +3,7 @@ from flask import Flask
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate from flask_migrate import Migrate
from phytopi.config import Config from phytopi.config import Config
# from phytopi.camera.camera_pi import Camera
app = Flask(__name__, instance_relative_config=True) app = Flask(__name__, instance_relative_config=True)
@ -10,11 +11,6 @@ app.config.from_object(Config)
db = SQLAlchemy(app) db = SQLAlchemy(app)
migrate = Migrate(app, db) 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 from phytopi import routes, models

View file

@ -1,5 +0,0 @@
from flask import Blueprint
bp = Blueprint('camera', __name__)
from phytopi.camera import camera

View file

@ -1,90 +0,0 @@
import io
import os
import sys
import stat
import subprocess
import signal
from time import sleep
from enum import Enum
from PIL import Image
from phytopi import db, models
from phytopi.models import CameraSettings
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'):
self.pidfile = pidfile
self.executable = executable
self.fifo = fifo
self.proc = None
self.pid = None
self.status = CameraStatus.STOPPED
def start(self):
# 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], preexec_fn=os.setsid)
self.pid = self.proc.pid
with open(self.pidfile, 'w') as fh:
fh.write(str(self.pid))
self.status = CameraStatus.IDLE
def stop(self):
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('{}\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:
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.status = CameraStatus.IDLE
else:
raise CameraActionError('Camera not in timelapse mode!')
def get_frame(self, thumbnail=False):
output = io.BytesIO()
img = Image.open('/dev/shm/mjpeg/cam.jpg')
img.save(output, format='JPEG')
output.seek(0, 0)
return output.getvalue()

View file

@ -1,5 +0,0 @@
from flask import Blueprint
bp = Blueprint('errors', __name__)
from phytopi.errors import handlers

View file

@ -1,8 +1,6 @@
from datetime import datetime from datetime import datetime
from phytopi import db from phytopi import db
class Dataset(db.Model): class Dataset(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(64), index=True) title = db.Column(db.String(64), index=True)
@ -31,16 +29,17 @@ class CameraSettings(db.Model):
fps = db.Column(db.Integer, default=5) fps = db.Column(db.Integer, default=5)
width = db.Column(db.Integer, default=3280) width = db.Column(db.Integer, default=3280)
height = db.Column(db.Integer, default=2464) height = db.Column(db.Integer, default=2464)
exposure_mode = db.Column(db.String(64), default='auto') fix_shutter = db.Column(db.Boolean, default=False)
fix_wb = db.Column(db.Boolean, default=False)
def __init__(self, name, iso=100, fps=5, width=3280, height=2464, exposure_mode='auto'): def __init__(self, name, iso=100, fps=5, width=3280, height=2464, fix_shutter=False, fix_wb=False):
self.name = name self.name = name
self.iso = iso self.iso = iso
self.fps = fps self.fps = fps
self.width = width self.width = width
self.height = height self.height = height
self.exposure_mode = exposure_mode self.fix_shutter = fix_shutter
self.fix_wb = fix_wb
def __repr__(self): def __repr__(self):
return '<CameraSettings {}>'.format(self.name) return '<CameraSettings {}>'.format(self.name)

BIN
phytopi/phytopi.db Normal file

Binary file not shown.

View file

@ -1,6 +1,6 @@
from flask import render_template, Response, flash, jsonify, request, stream_with_context, send_file from flask import render_template, Response, flash, jsonify, request, stream_with_context, send_file
from phytopi.camera.camera import CameraWorker, CameraStatus from phytopi.camera.camera_pi import Camera
from phytopi.forms import CameraSettingsForm from phytopi.forms import CameraSettingsForm
from phytopi import app from phytopi import app
@ -10,59 +10,46 @@ from phytopi.models import Dataset
from io import BytesIO from io import BytesIO
import zipstream import zipstream
camera = CameraWorker(app=app) camera = Camera(app=app)
camera.start() # camera = app.camera
save_frames = False
@app.route('/') @app.route('/')
@app.route('/index') @app.route('/index')
def index(): def index():
global camera content = {'timelapse': save_frames}
if camera.status == CameraStatus.TIMELAPSE:
timelapse = True
else:
timelapse = False
content = {'timelapse': timelapse}
return render_template('index.html', content=content) return render_template('index.html', content=content)
@app.route('/toggle_timelapse', methods=['GET', 'POST']) @app.route('/toggle_timelapse', methods=['GET', 'POST'])
def start_stop_timelapse(): def start_stop_timelapse():
# global save_frames global save_frames
global current_dataset global current_dataset
global camera global camera
# if save_frames: if save_frames:
# save_frames = False save_frames = False
# timelapse_interval = None 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:
btn_text = "Stop"
btn_class = 'btn-danger'
camera.start_timelapse()
elif camera.status == CameraStatus.TIMELAPSE:
btn_text = "Start" btn_text = "Start"
btn_class = 'btn-primary' btn_class = 'btn-primary'
camera.stop_timelapse() 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)
return jsonify(btn_text=btn_text, btn_class=btn_class) return jsonify(btn_text=btn_text, btn_class=btn_class)
def gen(camera): def gen(camera):
# def gen():
"""Video streaming generator function.""" """Video streaming generator function."""
while True: while True:
frame = camera.get_frame(thumbnail=True) frame = camera.get_frame(thumbnail=True)

19
requirements_pi.txt Normal file
View file

@ -0,0 +1,19 @@
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