def settings_users(): """ Display user settings """ if not logged_in(): return redirect(url_for('general_routes.home')) if session['user_group'] == 'guest': flaskutils.deny_guest_user() return redirect(url_for('general_routes.home')) users = db_retrieve_table( current_app.config['USER_DB_PATH'], Users, entry='all') form_add_user = flaskforms.AddUser() form_mod_user = flaskforms.ModUser() form_del_user = flaskforms.DelUser() if request.method == 'POST': form_name = request.form['form-name'] if form_name == 'addUser': flaskutils.user_add(form_add_user) elif form_name == 'delUser': if flaskutils.user_del(form_del_user) == 'logout': return redirect('/logout') elif form_name == 'modUser': if flaskutils.user_mod(form_mod_user) == 'logout': return redirect('/logout') return redirect('/settings/users') return render_template('settings/users.html', users=users, form_add_user=form_add_user, form_mod_user=form_mod_user, form_del_user=form_del_user)
def admin_statistics(): """ Display collected statistics """ if not logged_in(): return redirect(url_for('general_routes.home')) if session['user_group'] == 'guest': flaskutils.deny_guest_user() return redirect(url_for('general_routes.home')) try: statistics = return_stat_file_dict(STATS_CSV) except IOError: statistics = {} return render_template('admin/statistics.html', statistics=statistics)
def page_logview(): """ Display the last (n) lines from a log file """ if not logged_in(): return redirect(url_for('general_routes.home')) form_log_view = flaskforms.LogView() log_output = None lines = 30 logfile = '' if request.method == 'POST': if session['user_group'] == 'guest': flaskutils.deny_guest_user() return redirect('/logview') if form_log_view.lines.data: lines = form_log_view.lines.data if form_log_view.loglogin.data: logfile = LOGIN_LOG_FILE elif form_log_view.loghttp.data: logfile = HTTP_LOG_FILE elif form_log_view.logdaemon.data: logfile = DAEMON_LOG_FILE elif form_log_view.logupgrade.data: logfile = UPGRADE_LOG_FILE elif form_log_view.logrestore.data: logfile = RESTORE_LOG_FILE # Get contents from file if os.path.isfile(logfile): log = subprocess.Popen('tail -n ' + str(lines) + ' ' + logfile, stdout=subprocess.PIPE, shell=True) (log_output, _) = log.communicate() log.wait() else: log_output = 404 return render_template('tools/logview.html', form_log_view=form_log_view, lines=lines, logfile=logfile, log_output=log_output)
def admin_backup(): """ Load the backup management page """ if not logged_in(): return redirect(url_for('general_routes.home')) if session['user_group'] == 'guest': flaskutils.deny_guest_user() return redirect(url_for('general_routes.home')) form_backup = flaskforms.Backup() backup_dirs = [] if not os.path.isdir('/var/Mycodo-backups'): flash("Error: Backup directory doesn't exist.", "error") else: backup_dirs = sorted(next(os.walk('/var/Mycodo-backups'))[1]) backup_dirs.reverse() backup_dirs_filtered = [] for each_dir in backup_dirs: if each_dir.startswith("Mycodo-backup-"): backup_dirs_filtered.append(each_dir) if request.method == 'POST': form_name = request.form['form-name'] if form_name == 'restore': if form_backup.restore.data: flash("Restore functionality is not currently enabled.", "error") # formUpdate.restore.data # restore_command = '{path}/mycodo/scripts/mycodo_wrapper ' \ # 'restore >> /var/log/mycodo/mycodorestore.log 2>&1'.format( # path=INSTALL_DIRECTORY) # subprocess.Popen(restore_command, shell=True) return render_template('admin/backup.html', form_backup=form_backup, backup_dirs=backup_dirs_filtered)
def settings_alerts(): """ Display alert settings """ if not logged_in(): return redirect(url_for('general_routes.home')) if session['user_group'] == 'guest': flaskutils.deny_guest_user() return redirect('/settings') smtp = db_retrieve_table( current_app.config['MYCODO_DB_PATH'], SMTP, entry='first') form_email_alert = flaskforms.EmailAlert() if request.method == 'POST': form_name = request.form['form-name'] # Update smtp settings table in mycodo SQL database if form_name == 'EmailAlert': flaskutils.settings_alert_mod(form_email_alert) return redirect('/settings/alerts') return render_template('settings/alerts.html', smtp=smtp, form_email_alert=form_email_alert)
def admin_upgrade(): """ Display any available upgrades and option to upgrade """ if not logged_in(): return redirect(url_for('general_routes.home')) if session['user_group'] == 'guest': flaskutils.deny_guest_user() return redirect(url_for('general_routes.home')) if not internet(): flash( gettext("Upgrade functionality is disabled because an internet " "connection was unable to be detected"), "error") return render_template('admin/upgrade.html', is_internet=False) # Read from the upgrade status file created by the upgrade script # to indicate if the upgrade is running. try: with open(INSTALL_DIRECTORY + '/.upgrade') as f: upgrade = int(f.read(1)) except IOError: try: with open(INSTALL_DIRECTORY + '/.upgrade', 'w') as f: f.write('0') finally: upgrade = 0 if upgrade: if upgrade == 1: flash( gettext("An upgrade is currently in progress. Please wait " "for it to finish"), "error") elif upgrade == 2: flash( gettext("There was an error encountered during the upgrade " "process. Check the upgrade log for details."), "error") return render_template('admin/upgrade.html', upgrade=upgrade) form_backup = flaskforms.Backup() form_upgrade = flaskforms.Upgrade() is_internet = True upgrade_available = False # Check for any new Mycodo releases on github releases = github_releases(4) if len(releases) > 0: latest_release = releases[0] current_releases = [] releases_behind = None for index, each_release in enumerate(releases): if parse_version(each_release) >= parse_version(MYCODO_VERSION): current_releases.append(each_release) if parse_version(each_release) == parse_version(MYCODO_VERSION): releases_behind = index if parse_version(releases[0]) > parse_version(MYCODO_VERSION): upgrade_available = True else: current_releases = [] latest_release = '0.0.0' releases_behind = 0 if request.method == 'POST': if form_upgrade.upgrade.data and upgrade_available: subprocess.Popen('{path}/mycodo/scripts/mycodo_wrapper upgrade >>' ' /var/log/mycodo/mycodoupgrade.log 2>&1'.format( path=INSTALL_DIRECTORY), shell=True) upgrade = 1 flash( gettext("The upgrade has started. The daemon will be " "stopped during the upgrade."), "success") else: flash(gettext("You cannot upgrade if an upgrade is not available"), "error") return render_template('admin/upgrade.html', form_backup=form_backup, form_upgrade=form_upgrade, current_release=MYCODO_VERSION, current_releases=current_releases, latest_release=latest_release, releases_behind=releases_behind, upgrade_available=upgrade_available, upgrade=upgrade, is_internet=is_internet)
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)
def page_pid(): """ Display PID settings """ if not logged_in(): return redirect(url_for('general_routes.home')) pids = db_retrieve_table( current_app.config['MYCODO_DB_PATH'], PID, entry='all') relay = db_retrieve_table( current_app.config['MYCODO_DB_PATH'], Relay, entry='all') sensor = db_retrieve_table( current_app.config['MYCODO_DB_PATH'], Sensor, entry='all') display_order_unsplit = db_retrieve_table( current_app.config['MYCODO_DB_PATH'], DisplayOrder, entry='first').pid if display_order_unsplit: display_order = display_order_unsplit.split(",") else: display_order = [] form_add_pid = flaskforms.AddPID() form_mod_pid = flaskforms.ModPID() method = db_retrieve_table( current_app.config['MYCODO_DB_PATH'], Method) method = method.filter( Method.method_order == 0).all() if request.method == 'POST': if session['user_group'] == 'guest': flaskutils.deny_guest_user() return redirect('/pid') form_name = request.form['form-name'] if form_name == 'addPID': flaskutils.pid_add(form_add_pid, display_order) elif form_name == 'modPID': if form_mod_pid.mod_pid_del.data: flaskutils.pid_del( form_mod_pid.modPID_id.data, display_order) elif form_mod_pid.mod_pid_order_up.data: flaskutils.pid_reorder( form_mod_pid.modPID_id.data, display_order, 'up') elif form_mod_pid.mod_pid_order_down.data: flaskutils.pid_reorder( form_mod_pid.modPID_id.data, display_order, 'down') elif form_mod_pid.mod_pid_activate.data: flaskutils.pid_activate( form_mod_pid.modPID_id.data) elif form_mod_pid.mod_pid_deactivate.data: flaskutils.pid_deactivate( form_mod_pid.modPID_id.data) elif form_mod_pid.mod_pid_hold.data: flaskutils.pid_manipulate( 'Hold', form_mod_pid.modPID_id.data) elif form_mod_pid.mod_pid_pause.data: flaskutils.pid_manipulate( 'Pause', form_mod_pid.modPID_id.data) elif form_mod_pid.mod_pid_resume.data: flaskutils.pid_manipulate( 'Resume', form_mod_pid.modPID_id.data) else: flaskutils.pid_mod(form_mod_pid) return redirect('/pid') return render_template('pages/pid.html', method=method, pids=pids, relay=relay, sensor=sensor, displayOrder=display_order, form_add_pid=form_add_pid, form_mod_pid=form_mod_pid)