예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
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)