def page_camera(): """ Page to start/stop video stream or time-lapse, or capture a still image. Displays most recent still image and time-lapse image. """ if not flaskutils.user_has_permission('view_camera'): return redirect(url_for('general_routes.home')) form_camera = flaskforms.Camera() camera = Camera.query.all() # Check if a video stream is active for each_camera in camera: if each_camera.stream_started and not CameraStream().is_running(): each_camera.stream_started = False db.session.commit() if request.method == 'POST': if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('page_routes.page_camera')) control = DaemonControl() mod_camera = Camera.query.filter( Camera.id == form_camera.camera_id.data).first() if form_camera.capture_still.data: if mod_camera.stream_started: flash( gettext( u"Cannot capture still image if stream is active.")) return redirect('/camera') if CameraStream().is_running(): CameraStream().terminate_controller() # Stop camera stream time.sleep(2) camera_record('photo', mod_camera) elif form_camera.start_timelapse.data: if mod_camera.stream_started: flash(gettext(u"Cannot start time-lapse if stream is active.")) return redirect('/camera') now = time.time() mod_camera.timelapse_started = True mod_camera.timelapse_start_time = now mod_camera.timelapse_end_time = now + float( form_camera.timelapse_runtime_sec.data) mod_camera.timelapse_interval = form_camera.timelapse_interval.data mod_camera.timelapse_next_capture = now mod_camera.timelapse_capture_number = 0 db.session.commit() control.refresh_daemon_camera_settings() elif form_camera.pause_timelapse.data: mod_camera.timelapse_paused = True db.session.commit() control.refresh_daemon_camera_settings() elif form_camera.resume_timelapse.data: mod_camera.timelapse_paused = False db.session.commit() control.refresh_daemon_camera_settings() elif form_camera.stop_timelapse.data: mod_camera.timelapse_started = False mod_camera.timelapse_start_time = None mod_camera.timelapse_end_time = None mod_camera.timelapse_interval = None mod_camera.timelapse_next_capture = None mod_camera.timelapse_capture_number = None db.session.commit() control.refresh_daemon_camera_settings() elif form_camera.start_stream.data: if mod_camera.timelapse_started: flash(gettext(u"Cannot start stream if time-lapse is active.")) return redirect('/camera') elif CameraStream().is_running(): flash( gettext( u"Cannot start stream. The stream is already running.") ) return redirect('/camera') elif (not (mod_camera.camera_type == 'Raspberry Pi' and mod_camera.library == 'picamera')): flash( gettext(u"Streaming is only supported with the Raspberry" u" Pi camera using the picamera library.")) return redirect('/camera') elif Camera.query.filter_by(stream_started=True).count(): flash( gettext(u"Cannot start stream if another stream is " u"already in progress.")) return redirect('/camera') else: mod_camera.stream_started = True db.session.commit() elif form_camera.stop_stream.data: if CameraStream().is_running(): CameraStream().terminate_controller() mod_camera.stream_started = False db.session.commit() return redirect('/camera') # Get the full path and timestamps of latest still and time-lapse images latest_img_still_ts = {} latest_img_still = {} latest_img_tl_ts = {} latest_img_tl = {} for each_camera in camera: camera_path = os.path.join( PATH_CAMERAS, '{id}-{uid}'.format(id=each_camera.id, uid=each_camera.unique_id)) try: latest_still_img_full_path = max(glob.iglob( '{path}/still/Still-{cam_id}-*.jpg'.format( path=camera_path, cam_id=each_camera.id)), key=os.path.getmtime) except ValueError: latest_still_img_full_path = None if latest_still_img_full_path: ts = os.path.getmtime(latest_still_img_full_path) latest_img_still_ts[ each_camera.id] = datetime.datetime.fromtimestamp(ts).strftime( "%Y-%m-%d %H:%M:%S") latest_img_still[each_camera.id] = os.path.basename( latest_still_img_full_path) else: latest_img_still[each_camera.id] = None try: latest_time_lapse_img_full_path = max(glob.iglob( '{path}/timelapse/Timelapse-{cam_id}-*.jpg'.format( path=camera_path, cam_id=each_camera.id)), key=os.path.getmtime) except ValueError: latest_time_lapse_img_full_path = None if latest_time_lapse_img_full_path: ts = os.path.getmtime(latest_time_lapse_img_full_path) latest_img_tl_ts[each_camera.id] = datetime.datetime.fromtimestamp( ts).strftime("%Y-%m-%d %H:%M:%S") latest_img_tl[each_camera.id] = os.path.basename( latest_time_lapse_img_full_path) else: latest_img_tl[each_camera.id] = None time_now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') return render_template('pages/camera.html', camera=camera, form_camera=form_camera, latest_img_still=latest_img_still, latest_img_still_ts=latest_img_still_ts, latest_img_tl=latest_img_tl, latest_img_tl_ts=latest_img_tl_ts, time_now=time_now)
def page_camera(): """ Page to start/stop video stream or time-lapse, or capture a still image. Displays most recent still image and time-lapse image. """ if not logged_in(): return redirect(url_for('general_routes.home')) form_camera = flaskforms.Camera() camera_enabled = False try: if 'start_x=1' in open('/boot/config.txt').read(): camera_enabled = True else: flash(gettext("Camera support doesn't appear to be enabled. " "Please enable it with 'sudo raspi-config'"), "error") except IOError as e: logger.error("Camera IOError raised in '/camera' endpoint: " "{err}".format(err=e)) # Check if a video stream is active stream_locked = os.path.isfile(LOCK_FILE_STREAM) if stream_locked and not CameraStream().is_running(): os.remove(LOCK_FILE_STREAM) stream_locked = os.path.isfile(LOCK_FILE_STREAM) if request.method == 'POST': form_name = request.form['form-name'] if session['user_group'] == 'guest': flaskutils.deny_guest_user() return redirect('/camera') elif form_name == 'camera': if form_camera.Still.data: if not stream_locked: try: if CameraStream().is_running(): CameraStream().terminate_controller() # Stop camera stream time.sleep(2) camera = db_retrieve_table( current_app.config['MYCODO_DB_PATH'], CameraStill, entry='first') camera_record('photo', camera) except Exception as msg: flash("Camera Error: {}".format(msg), "error") else: flash(gettext("Cannot capture still if stream is active. " "If it is not active, delete %(file)s.", file=LOCK_FILE_STREAM), "error") elif form_camera.StartTimelapse.data: if not stream_locked: # Create lock file and file with time-lapse parameters open(LOCK_FILE_TIMELAPSE, 'a') # Save time-lapse parameters to a csv file to resume # if there is a power outage or reboot. now = time.time() timestamp = datetime.datetime.now().strftime( '%Y-%m-%d_%H-%M-%S') uid_gid = pwd.getpwnam('mycodo').pw_uid timelapse_data = [ ['start_time', timestamp], ['end_time', now + float(form_camera.TimelapseRunTime.data)], ['interval', form_camera.TimelapseInterval.data], ['next_capture', now], ['capture_number', 0]] with open(FILE_TIMELAPSE_PARAM, 'w') as time_lapse_file: write_csv = csv.writer(time_lapse_file) for row in timelapse_data: write_csv.writerow(row) os.chown(FILE_TIMELAPSE_PARAM, uid_gid, uid_gid) os.chmod(FILE_TIMELAPSE_PARAM, 0664) else: flash(gettext("Cannot start time-lapse if a video stream " "is active. If it is not active, delete " "%(file)s.", file=LOCK_FILE_STREAM), "error") elif form_camera.StopTimelapse.data: try: os.remove(FILE_TIMELAPSE_PARAM) os.remove(LOCK_FILE_TIMELAPSE) except IOError as e: logger.error("Camera IOError raised in '/camera' " "endpoint: {err}".format(err=e)) elif form_camera.StartStream.data: if not is_time_lapse_locked(): open(LOCK_FILE_STREAM, 'a') stream_locked = True else: flash(gettext("Cannot start stream if a time-lapse is " "active. If not active, delete %(file)s.", file=LOCK_FILE_TIMELAPSE), "error") elif form_camera.StopStream.data: if CameraStream().is_running(): CameraStream().terminate() if os.path.isfile(LOCK_FILE_STREAM): os.remove(LOCK_FILE_STREAM) stream_locked = False # Get the full path of latest still image try: latest_still_img_full_path = max(glob.iglob( '{path}/camera-stills/*.jpg'.format(path=INSTALL_DIRECTORY)), key=os.path.getmtime) ts = os.path.getmtime(latest_still_img_full_path) latest_still_img_ts = datetime.datetime.fromtimestamp(ts).strftime("%c") latest_still_img = os.path.basename(latest_still_img_full_path) except Exception as e: logger.error( "Exception raised in '/camera' endpoint: {err}".format(err=e)) latest_still_img_ts = None latest_still_img = None # Get the full path of latest timelapse image try: latest_time_lapse_img_full_path = max(glob.iglob( '{path}/camera-timelapse/*.jpg'.format(path=INSTALL_DIRECTORY)), key=os.path.getmtime) ts = os.path.getmtime(latest_time_lapse_img_full_path) latest_time_lapse_img_ts = datetime.datetime.fromtimestamp(ts).strftime("%c") latest_time_lapse_img = os.path.basename( latest_time_lapse_img_full_path) except Exception as e: logger.error( "Exception raised in '/camera' endpoint: {err}".format(err=e)) latest_time_lapse_img_ts = None latest_time_lapse_img = None # If time-lapse active, retrieve parameters for display dict_time_lapse = {} time_now = datetime.datetime.now().strftime('%c') if (os.path.isfile(FILE_TIMELAPSE_PARAM) and os.path.isfile(LOCK_FILE_TIMELAPSE)): with open(FILE_TIMELAPSE_PARAM, mode='r') as infile: reader = csv.reader(infile) dict_time_lapse = OrderedDict((row[0], row[1]) for row in reader) dict_time_lapse['start_time'] = datetime.datetime.strptime( dict_time_lapse['start_time'], "%Y-%m-%d_%H-%M-%S") dict_time_lapse['start_time'] = dict_time_lapse['start_time'].strftime('%c') dict_time_lapse['end_time'] = datetime.datetime.fromtimestamp( float(dict_time_lapse['end_time'])).strftime('%c') dict_time_lapse['next_capture'] = datetime.datetime.fromtimestamp( float(dict_time_lapse['next_capture'])).strftime('%c') return render_template('pages/camera.html', camera_enabled=camera_enabled, form_camera=form_camera, latest_still_img_ts=latest_still_img_ts, latest_still_img=latest_still_img, latest_time_lapse_img_ts=latest_time_lapse_img_ts, latest_time_lapse_img=latest_time_lapse_img, stream_locked=stream_locked, time_lapse_locked=is_time_lapse_locked(), time_now=time_now, tl_parameters_dict=dict_time_lapse)