def settings_users(): """ Display user settings """ if not flaskutils.user_has_permission('view_settings'): return redirect(url_for('general_routes.home')) users = User.query.all() user_roles = Role.query.all() form_add_user = flaskforms.UserAdd() form_mod_user = flaskforms.UserMod() form_user_roles = flaskforms.UserRoles() if request.method == 'POST': if not flaskutils.user_has_permission('edit_users'): return redirect(url_for('general_routes.home')) if form_add_user.add_user.data: flaskutils.user_add(form_add_user) elif form_mod_user.delete.data: if flaskutils.user_del(form_mod_user) == 'logout': return redirect('/logout') elif form_mod_user.save.data: if flaskutils.user_mod(form_mod_user) == 'logout': return redirect('/logout') elif (form_user_roles.add_role.data or form_user_roles.save_role.data or form_user_roles.delete_role.data): flaskutils.user_roles(form_user_roles) return redirect(url_for('settings_routes.settings_users')) return render_template('settings/users.html', themes=THEMES, users=users, user_roles=user_roles, form_add_user=form_add_user, form_mod_user=form_mod_user, form_user_roles=form_user_roles)
def admin_backup(): """ Load the backup management page """ if not flaskutils.user_has_permission('edit_settings'): 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 page_usage(): """ Display relay usage (duration and energy usage/cost) """ if not flaskutils.user_has_permission('view_stats'): return redirect(url_for('general_routes.home')) misc = Misc.query.first() relay = Relay.query.all() relay_stats = return_relay_usage(misc, relay) day = misc.relay_usage_dayofmonth if 4 <= day <= 20 or 24 <= day <= 30: date_suffix = 'th' else: date_suffix = ['st', 'nd', 'rd'][day % 10 - 1] display_order = csv_to_list_of_int(DisplayOrder.query.first().relay) return render_template('pages/usage.html', date_suffix=date_suffix, display_order=display_order, misc=misc, relay=relay, relay_stats=relay_stats, timestamp=time.strftime("%c"))
def computer_command(action): """Execute one of several commands as root""" if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('general_routes.home')) try: if action not in ['restart', 'shutdown']: flash("Unrecognized command: {action}".format(action=action), "success") return redirect('/settings') cmd = '{path}/mycodo/scripts/mycodo_wrapper {action} 2>&1'.format( path=INSTALL_DIRECTORY, action=action) subprocess.Popen(cmd, shell=True) if action == 'restart': flash(gettext(u"System rebooting in 10 seconds"), "success") elif action == 'shutdown': flash(gettext(u"System shutting down in 10 seconds"), "success") return redirect('/settings') except Exception as e: logger.error("System command '{cmd}' raised and error: " "{err}".format(cmd=action, err=e)) flash( "System command '{cmd}' raised and error: " "{err}".format(cmd=action, err=e), "error") return redirect(url_for('general_routes.home'))
def settings_camera(): """ Display camera settings """ if not flaskutils.user_has_permission('view_settings'): return redirect(url_for('general_routes.home')) form_camera = flaskforms.SettingsCamera() camera = Camera.query.all() relay = Relay.query.all() camera_libraries = [] camera_types = [] for camera_type, library in CAMERAS.items(): camera_libraries.append(library) camera_types.append(camera_type) opencv_devices = count_cameras_opencv() pi_camera_enabled = False try: if 'start_x=1' in open('/boot/config.txt').read(): pi_camera_enabled = True except IOError as e: logger.error("Camera IOError raised in '/settings/camera' endpoint: " "{err}".format(err=e)) if request.method == 'POST': if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('general_routes.home')) if form_camera.camera_add.data: flaskutils.camera_add(form_camera) elif form_camera.camera_mod.data: flaskutils.camera_mod(form_camera) elif form_camera.camera_del.data: flaskutils.camera_del(form_camera) return redirect(url_for('settings_routes.settings_camera')) return render_template('settings/camera.html', camera=camera, camera_libraries=camera_libraries, camera_types=camera_types, form_camera=form_camera, opencv_devices=opencv_devices, pi_camera_enabled=pi_camera_enabled, relay=relay)
def page_pid(): """ Display PID settings """ pids = PID.query.all() relay = Relay.query.all() sensor = Sensor.query.all() display_order = csv_to_list_of_int(DisplayOrder.query.first().pid) form_add_pid = flaskforms.PIDAdd() form_mod_pid = flaskforms.PIDMod() method = Method.query.all() if request.method == 'POST': if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('general_routes.home')) form_name = request.form['form-name'] if form_name == 'addPID': flaskutils.pid_add(form_add_pid) elif form_name == 'modPID': if form_mod_pid.delete.data: flaskutils.pid_del( form_mod_pid.pid_id.data) elif form_mod_pid.reorder_up.data: flaskutils.pid_reorder( form_mod_pid.pid_id.data, display_order, 'up') elif form_mod_pid.reorder_down.data: flaskutils.pid_reorder( form_mod_pid.pid_id.data, display_order, 'down') elif form_mod_pid.activate.data: flaskutils.pid_activate( form_mod_pid.pid_id.data) elif form_mod_pid.deactivate.data: flaskutils.pid_deactivate( form_mod_pid.pid_id.data) elif form_mod_pid.hold.data: flaskutils.pid_manipulate( form_mod_pid.pid_id.data, 'Hold') elif form_mod_pid.pause.data: flaskutils.pid_manipulate( form_mod_pid.pid_id.data, 'Pause') elif form_mod_pid.resume.data: flaskutils.pid_manipulate( form_mod_pid.pid_id.data, 'Resume') 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)
def admin_backup(): """ Load the backup management page """ if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('general_routes.home')) form_backup = flaskforms.Backup() backup_dirs_tmp = [] if not os.path.isdir('/var/Mycodo-backups'): flash("Error: Backup directory doesn't exist.", "error") else: backup_dirs_tmp = sorted(next(os.walk(BACKUP_PATH))[1]) backup_dirs_tmp.reverse() backup_dirs = [] full_paths = [] for each_dir in backup_dirs_tmp: if each_dir.startswith("Mycodo-backup-"): backup_dirs.append(each_dir) full_paths.append(os.path.join(BACKUP_PATH, each_dir)) if request.method == 'POST': if form_backup.backup.data: cmd = '{pth}/mycodo/scripts/mycodo_wrapper backup-create ' \ ' >> {log} 2>&1'.format(pth=INSTALL_DIRECTORY, log=BACKUP_LOG_FILE) subprocess.Popen(cmd, shell=True) flash(gettext(u"Backup in progress. It should complete within a " u"few seconds to a few minutes. The backup will " u"appear on this page after it completes."), "success") elif form_backup.delete.data: cmd = '{pth}/mycodo/scripts/mycodo_wrapper backup-delete {dir}' \ ' 2>&1'.format(pth=INSTALL_DIRECTORY, dir=form_backup.selected_dir.data) subprocess.Popen(cmd, shell=True) flash(gettext(u"Deletion of backup in progress. It should " u"complete within a few seconds to a few minutes. " u"The backup will disappear on this page after it " u"completes."), "success") elif form_backup.restore.data: cmd = '{pth}/mycodo/scripts/mycodo_wrapper backup-restore ' \ '{backup} >> {log} 2>&1'.format( pth=INSTALL_DIRECTORY, backup=form_backup.full_path.data, log=RESTORE_LOG_FILE) subprocess.Popen(cmd, shell=True) flash(gettext(u"Restore in progress. It should complete within a " u"few seconds to a few minutes."), "success") return render_template('admin/backup.html', form_backup=form_backup, backup_dirs=backup_dirs, full_paths=full_paths)
def admin_statistics(): """ Display collected statistics """ if not flaskutils.user_has_permission('view_stats'): 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_usage_reports(): """ Display relay usage (duration and energy usage/cost) """ if not flaskutils.user_has_permission('view_stats'): return redirect(url_for('general_routes.home')) report_location = os.path.normpath(USAGE_REPORTS_PATH) reports = [0, 0] return render_template('pages/usage_reports.html', report_location=report_location, reports=reports)
def settings_alerts(): """ Display alert settings """ if not flaskutils.user_has_permission('view_settings'): return redirect(url_for('general_routes.home')) smtp = SMTP.query.first() form_email_alert = flaskforms.EmailAlert() if request.method == 'POST': if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('general_routes.home')) form_name = request.form['form-name'] if form_name == 'EmailAlert': flaskutils.settings_alert_mod(form_email_alert) return redirect(url_for('settings_routes.settings_alerts')) return render_template('settings/alerts.html', smtp=smtp, form_email_alert=form_email_alert)
def calibration_select(): """ Landing page to initially select the device to calibrate """ if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('general_routes.home')) form_calibration = flaskforms.Calibration() if form_calibration.submit.data: route = 'calibration_routes.{page}'.format( page=form_calibration.selection.data) return redirect(url_for(route)) return render_template('tools/calibration.html', form_calibration=form_calibration)
def settings_general(): """ Display general settings """ if not flaskutils.user_has_permission('view_settings'): return redirect(url_for('general_routes.home')) misc = Misc.query.first() form_settings_general = flaskforms.SettingsGeneral() languages_sorted = sorted(LANGUAGES.items(), key=operator.itemgetter(1)) if request.method == 'POST': if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('general_routes.home')) form_name = request.form['form-name'] if form_name == 'General': flaskutils.settings_general_mod(form_settings_general) return redirect(url_for('settings_routes.settings_general')) return render_template('settings/general.html', misc=misc, languages=languages_sorted, form_settings_general=form_settings_general)
def page_lcd(): """ Display LCD output settings """ lcd = LCD.query.all() pid = PID.query.all() relay = Relay.query.all() sensor = Sensor.query.all() display_order = csv_to_list_of_int(DisplayOrder.query.first().lcd) form_add_lcd = flaskforms.LCDAdd() form_mod_lcd = flaskforms.LCDMod() if request.method == 'POST': if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('general_routes.home')) if form_add_lcd.add.data: flaskutils.lcd_add(form_add_lcd.quantity.data) elif form_mod_lcd.save.data: flaskutils.lcd_mod(form_mod_lcd) elif form_mod_lcd.delete.data: flaskutils.lcd_del(form_mod_lcd.lcd_id.data) elif form_mod_lcd.reorder_up.data: flaskutils.lcd_reorder(form_mod_lcd.lcd_id.data, display_order, 'up') elif form_mod_lcd.reorder_down.data: flaskutils.lcd_reorder(form_mod_lcd.lcd_id.data, display_order, 'down') elif form_mod_lcd.activate.data: flaskutils.lcd_activate(form_mod_lcd.lcd_id.data) elif form_mod_lcd.deactivate.data: flaskutils.lcd_deactivate(form_mod_lcd.lcd_id.data) elif form_mod_lcd.reset_flashing.data: flaskutils.lcd_reset_flashing(form_mod_lcd.lcd_id.data) return redirect('/lcd') return render_template('pages/lcd.html', lcd=lcd, measurements=MEASUREMENTS, pid=pid, relay=relay, sensor=sensor, displayOrder=display_order, form_add_lcd=form_add_lcd, form_mod_lcd=form_mod_lcd)
def calibration_atlas_ph_measure(sensor_id): """ Acquire a measurement from the Atlas Scientific pH sensor and return it Used during calibration to display the current pH to the user """ if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('page_routes.page_atlas_ph_calibrate')) selected_sensor = Sensor.query.filter_by(unique_id=sensor_id).first() ph = None error = None if selected_sensor.interface == 'UART': ph_sensor_uart = AtlasScientificUART( selected_sensor.device_loc, baudrate=selected_sensor.baud_rate) lines = ph_sensor_uart.query('R') logger.debug("All Lines: {lines}".format(lines=lines)) if 'check probe' in lines: error = '"check probe" returned from sensor' elif not lines: error = 'Nothing returned from sensor' elif str_is_float(lines[0]): ph = lines[0] logger.debug('Value[0] is float: {val}'.format(val=ph)) else: error = 'Value[0] is not float or "check probe": {val}'.format( val=lines[0]) elif selected_sensor.interface == 'I2C': ph_sensor_i2c = AtlasScientificI2C( i2c_address=selected_sensor.i2c_address, i2c_bus=selected_sensor.i2c_bus) ph_status, ph_str = ph_sensor_i2c.query('R') if ph_status == 'error': error = "Sensor read unsuccessful: {err}".format(err=ph_str) elif ph_status == 'success': ph = ph_str if error: logger.error(error) return error, 204 else: return ph
def page_logview(): """ Display the last (n) lines from a log file """ if not flaskutils.user_has_permission('view_logs'): return redirect(url_for('general_routes.home')) form_log_view = flaskforms.LogView() log_output = None lines = 30 logfile = '' if request.method == 'POST': 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.logkeepup.data: logfile = KEEPUP_LOG_FILE elif form_log_view.logbackup.data: logfile = BACKUP_LOG_FILE elif form_log_view.logrestore.data: logfile = RESTORE_LOG_FILE elif form_log_view.logupgrade.data: logfile = UPGRADE_LOG_FILE # Get contents from file if os.path.isfile(logfile): command = 'tail -n {lines} {log}'.format(lines=lines, log=logfile) log = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) (log_output, _) = log.communicate() log.wait() log_output = unicode(log_output, 'utf-8') 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 page_timer(): """ Display Timer settings """ timer = Timer.query.all() relay = Relay.query.all() relay_choices = flaskutils.choices_id_name(relay) display_order = csv_to_list_of_int(DisplayOrder.query.first().timer) form_timer = flaskforms.Timer() if request.method == 'POST': if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('general_routes.home')) form_name = request.form['form-name'] if form_name == 'addTimer': flaskutils.timer_add(form_timer, request.form['timer_type'], display_order) elif form_name == 'modTimer': if form_timer.timerDel.data: flaskutils.timer_del(form_timer) elif form_timer.orderTimerUp.data: flaskutils.timer_reorder(form_timer.timer_id.data, display_order, 'up') elif form_timer.orderTimerDown.data: flaskutils.timer_reorder(form_timer.timer_id.data, display_order, 'down') elif form_timer.activate.data: flaskutils.timer_activate(form_timer) elif form_timer.deactivate.data: flaskutils.timer_deactivate(form_timer) elif form_timer.timerMod.data: flaskutils.timer_mod(form_timer) return redirect('/timer') return render_template('pages/timer.html', timer=timer, displayOrder=display_order, relay_choices=relay_choices, form_timer=form_timer)
def method_delete(method_id): """Delete a method""" action = '{action} {controller}'.format(action=gettext(u"Delete"), controller=gettext(u"Method")) if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('method_routes.method_list')) try: MethodData.query.filter( MethodData.method_id == int(method_id)).delete() Method.query.filter(Method.id == int(method_id)).delete() display_order = csv_to_list_of_int(DisplayOrder.query.first().method) display_order.remove(int(method_id)) DisplayOrder.query.first().method = list_to_csv(display_order) db.session.commit() flash("Success: {action}".format(action=action), "success") except Exception as except_msg: flash("Error: {action}: {err}".format(action=action, err=except_msg), "error") return redirect(url_for('method_routes.method_list'))
def remote_admin(page): """Return pages for remote administration""" if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('general_routes.home')) remote_hosts = Remote.query.all() display_order_unsplit = DisplayOrder.query.first().remote_host if display_order_unsplit: display_order = display_order_unsplit.split(",") else: display_order = [] if page == 'setup': form_setup = flaskforms.RemoteSetup() host_auth = {} for each_host in remote_hosts: host_auth[each_host.host] = flaskutils.auth_credentials( each_host.host, each_host.username, each_host.password_hash) if request.method == 'POST': form_name = request.form['form-name'] if form_name == 'setup': if form_setup.add.data: flaskutils.remote_host_add(form_setup=form_setup, display_order=display_order) if form_name == 'mod_remote': if form_setup.delete.data: flaskutils.remote_host_del(form_setup=form_setup) return redirect('/remote/setup') return render_template('remote/setup.html', form_setup=form_setup, display_order=display_order, remote_hosts=remote_hosts, host_auth=host_auth) else: return render_template('404.html'), 404
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_input(): """ Display sensor settings """ # TCA9548A I2C multiplexer multiplexer_addresses = [ '0x70', '0x71', '0x72', '0x73', '0x74', '0x75', '0x76', '0x77' ] multiplexer_channels = list(range(0, 9)) camera = Camera.query.all() lcd = LCD.query.all() pid = PID.query.all() relay = Relay.query.all() sensor = Sensor.query.all() user = User.query.all() conditional = Conditional.query.filter( Conditional.conditional_type == 'sensor').all() conditional_actions = ConditionalActions.query.all() display_order = csv_to_list_of_int(DisplayOrder.query.first().sensor) form_add_sensor = flaskforms.SensorAdd() form_mod_sensor = flaskforms.SensorMod() form_conditional = flaskforms.Conditional() form_conditional_actions = flaskforms.ConditionalActions() # If DS18B20 sensors added, compile a list of detected sensors ds18b20_sensors = [] if Sensor.query.filter(Sensor.device == 'DS18B20').count(): try: for each_sensor in W1ThermSensor.get_available_sensors(): ds18b20_sensors.append(each_sensor.id) except OSError: flash("Unable to detect sensors in '/sys/bus/w1/devices'", "error") # Create list of file names from the input_options directory # Used in generating the correct options for each sensor/device sensor_templates = [] sensor_path = os.path.join( INSTALL_DIRECTORY, 'mycodo/mycodo_flask/templates/pages/input_options') for (_, _, file_names) in os.walk(sensor_path): sensor_templates.extend(file_names) break if request.method == 'POST': if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('page_routes.page_input')) if form_add_sensor.sensorAddSubmit.data: flaskutils.sensor_add(form_add_sensor) elif form_mod_sensor.modSensorSubmit.data: flaskutils.sensor_mod(form_mod_sensor) elif form_mod_sensor.delSensorSubmit.data: flaskutils.sensor_del(form_mod_sensor) elif form_mod_sensor.orderSensorUp.data: flaskutils.sensor_reorder(form_mod_sensor.modSensor_id.data, display_order, 'up') elif form_mod_sensor.orderSensorDown.data: flaskutils.sensor_reorder(form_mod_sensor.modSensor_id.data, display_order, 'down') elif form_mod_sensor.activateSensorSubmit.data: flaskutils.sensor_activate(form_mod_sensor) elif form_mod_sensor.deactivateSensorSubmit.data: flaskutils.sensor_deactivate(form_mod_sensor) elif form_conditional.deactivate_cond.data: flaskutils.conditional_deactivate(form_conditional) elif form_conditional.activate_cond.data: flaskutils.conditional_activate(form_conditional) elif form_mod_sensor.sensorCondAddSubmit.data: flaskutils.conditional_add( 'sensor', 1, sensor_id=form_mod_sensor.modSensor_id.data) elif form_conditional.delete_cond.data: flaskutils.conditional_mod(form_conditional, 'delete') elif form_conditional.save_cond.data: flaskutils.conditional_mod(form_conditional, 'modify') elif form_conditional_actions.add_action.data: flaskutils.conditional_action_add(form_conditional_actions) elif form_conditional_actions.save_action.data: flaskutils.conditional_action_mod(form_conditional_actions, 'modify') elif form_conditional_actions.delete_action.data: flaskutils.conditional_action_mod(form_conditional_actions, 'delete') return redirect(url_for('page_routes.page_input')) return render_template('pages/input.html', camera=camera, conditional=conditional, conditional_actions=conditional_actions, conditional_actions_list=CONDITIONAL_ACTIONS, displayOrder=display_order, ds18b20_sensors=ds18b20_sensors, form_add_sensor=form_add_sensor, form_conditional=form_conditional, form_conditional_actions=form_conditional_actions, form_mod_sensor=form_mod_sensor, lcd=lcd, measurements=MEASUREMENTS, multiplexer_addresses=multiplexer_addresses, multiplexer_channels=multiplexer_channels, pid=pid, relay=relay, sensor=sensor, sensor_templates=sensor_templates, units=MEASUREMENT_UNITS, user=user)
def page_output(): """ Display relay status and config """ camera = Camera.query.all() lcd = LCD.query.all() relay = Relay.query.all() user = User.query.all() conditional = Conditional.query.filter( Conditional.conditional_type == 'relay').all() conditional_actions = ConditionalActions.query.all() display_order = csv_to_list_of_int(DisplayOrder.query.first().relay) form_add_relay = flaskforms.RelayAdd() form_mod_relay = flaskforms.RelayMod() form_conditional = flaskforms.Conditional() form_conditional_actions = flaskforms.ConditionalActions() # Create list of file names from the output_options directory # Used in generating the correct options for each relay/device relay_templates = [] relay_path = os.path.join( INSTALL_DIRECTORY, 'mycodo/mycodo_flask/templates/pages/output_options') for (_, _, file_names) in os.walk(relay_path): relay_templates.extend(file_names) break if request.method == 'POST': if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('page_routes.page_output')) if form_add_relay.relay_add.data: flaskutils.relay_add(form_add_relay) elif form_mod_relay.save.data: flaskutils.relay_mod(form_mod_relay) elif (form_mod_relay.turn_on.data or form_mod_relay.turn_off.data or form_mod_relay.on_submit.data): flaskutils.relay_on_off(form_mod_relay) elif form_mod_relay.delete.data: flaskutils.relay_del(form_mod_relay) elif form_mod_relay.order_up.data: flaskutils.relay_reorder(form_mod_relay.relay_id.data, display_order, 'up') elif form_mod_relay.order_down.data: flaskutils.relay_reorder(form_mod_relay.relay_id.data, display_order, 'down') elif form_conditional.add_cond.data: flaskutils.conditional_add(form_conditional.conditional_type.data, form_conditional.quantity.data) elif form_conditional.delete_cond.data: flaskutils.conditional_mod(form_conditional, 'delete') elif form_conditional.save_cond.data: flaskutils.conditional_mod(form_conditional, 'modify') elif form_conditional.activate_cond.data: flaskutils.conditional_activate(form_conditional) elif form_conditional.deactivate_cond.data: flaskutils.conditional_deactivate(form_conditional) elif form_conditional_actions.add_action.data: flaskutils.conditional_action_add(form_conditional_actions) elif form_conditional_actions.save_action.data: flaskutils.conditional_action_mod(form_conditional_actions, 'modify') elif form_conditional_actions.delete_action.data: flaskutils.conditional_action_mod(form_conditional_actions, 'delete') return redirect(url_for('page_routes.page_output')) return render_template('pages/output.html', camera=camera, conditional=conditional, conditional_actions=conditional_actions, conditional_actions_list=CONDITIONAL_ACTIONS, displayOrder=display_order, form_conditional=form_conditional, form_conditional_actions=form_conditional_actions, form_add_relay=form_add_relay, form_mod_relay=form_mod_relay, lcd=lcd, relay=relay, relay_templates=relay_templates, user=user)
def page_pid(): """ Display PID settings """ pids = PID.query.all() relay = Relay.query.all() sensor = Sensor.query.all() display_order = csv_to_list_of_int(DisplayOrder.query.first().pid) form_add_pid = flaskforms.PIDAdd() form_mod_pid_base = flaskforms.PIDModBase() form_mod_pid_relay = flaskforms.PIDModRelay() form_mod_pid_pwm = flaskforms.PIDModPWM() method = Method.query.all() # Create list of file names from the pid_options directory # Used in generating the correct options for each PID pid_templates = [] pid_path = os.path.join(INSTALL_DIRECTORY, 'mycodo/mycodo_flask/templates/pages/pid_options') for (_, _, file_names) in os.walk(pid_path): pid_templates.extend(file_names) break if request.method == 'POST': if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('general_routes.home')) form_name = request.form['form-name'] if form_name == 'addPID': flaskutils.pid_add(form_add_pid) elif form_name == 'modPID': if form_mod_pid_base.save.data: flaskutils.pid_mod(form_mod_pid_base, form_mod_pid_pwm, form_mod_pid_relay) elif form_mod_pid_base.delete.data: flaskutils.pid_del(form_mod_pid_base.pid_id.data) elif form_mod_pid_base.reorder_up.data: flaskutils.pid_reorder(form_mod_pid_base.pid_id.data, display_order, 'up') elif form_mod_pid_base.reorder_down.data: flaskutils.pid_reorder(form_mod_pid_base.pid_id.data, display_order, 'down') elif form_mod_pid_base.activate.data: flaskutils.pid_activate(form_mod_pid_base.pid_id.data) elif form_mod_pid_base.deactivate.data: flaskutils.pid_deactivate(form_mod_pid_base.pid_id.data) elif form_mod_pid_base.hold.data: flaskutils.pid_manipulate(form_mod_pid_base.pid_id.data, 'Hold') elif form_mod_pid_base.pause.data: flaskutils.pid_manipulate(form_mod_pid_base.pid_id.data, 'Pause') elif form_mod_pid_base.resume.data: flaskutils.pid_manipulate(form_mod_pid_base.pid_id.data, 'Resume') return redirect('/pid') return render_template('pages/pid.html', method=method, pids=pids, pid_templates=pid_templates, relay=relay, sensor=sensor, displayOrder=display_order, form_add_pid=form_add_pid, form_mod_pid_base=form_mod_pid_base, form_mod_pid_pwm=form_mod_pid_pwm, form_mod_pid_relay=form_mod_pid_relay)
def calibration_atlas_ph(): """ Step-by-step tool for calibrating the Atlas Scientific pH sensor """ if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('general_routes.home')) form_ph_calibrate = flaskforms.CalibrationAtlasph() sensor = Sensor.query.filter_by(device='ATLAS_PH_UART').all() stage = 0 next_stage = None selected_sensor = None sensor_device_name = None complete_with_error = None if form_ph_calibrate.hidden_next_stage.data is not None: next_stage = int(form_ph_calibrate.hidden_next_stage.data) # Clear Calibration memory if form_ph_calibrate.clear_calibration.data: selected_sensor = Sensor.query.filter_by( unique_id=form_ph_calibrate.selected_sensor_id.data).first() atlas_command = AtlasScientificCommand(selected_sensor) status, message = atlas_command.calibrate('clear_calibration') if status: flash(message, "error") else: flash(message, "success") # Begin calibration from Selected sensor elif form_ph_calibrate.go_from_first_stage.data: stage = 1 selected_sensor = Sensor.query.filter_by( unique_id=form_ph_calibrate.selected_sensor_id.data).first() if not selected_sensor: flash( 'Sensor not found: {}'.format( form_ph_calibrate.selected_sensor_id.data), 'error') else: for each_sensor in SENSORS: if selected_sensor.device == each_sensor[0]: sensor_device_name = each_sensor[1] # Continue calibration from selected sensor elif (form_ph_calibrate.go_to_next_stage.data or form_ph_calibrate.go_to_last_stage.data or next_stage > 1): selected_sensor = Sensor.query.filter_by( unique_id=form_ph_calibrate.hidden_sensor_id.data).first() for each_sensor in SENSORS: if selected_sensor.device == each_sensor[0]: sensor_device_name = each_sensor[1] if next_stage == 2: if form_ph_calibrate.temperature.data is None: flash( gettext( u"A valid temperature is required: %(temp)s is invalid.", temp=form_ph_calibrate.temperature.data), "error") stage = 1 else: temp = '{temp:.2f}'.format(temp=form_ph_calibrate.temperature.data) stage, complete_with_error = dual_commands_to_sensor( selected_sensor, 'temperature', temp, 'continuous', 1) elif next_stage == 3: stage, complete_with_error = dual_commands_to_sensor( selected_sensor, 'mid', '7.0', 'continuous', 2) elif next_stage == 4: stage, complete_with_error = dual_commands_to_sensor( selected_sensor, 'low', '4.0', 'continuous', 3) elif next_stage == 5: stage, complete_with_error = dual_commands_to_sensor( selected_sensor, 'high', '10.0', 'end', 4) return render_template('tools/calibration_atlas_ph.html', complete_with_error=complete_with_error, form_ph_calibrate=form_ph_calibrate, sensor=sensor, sensor_device_name=sensor_device_name, selected_sensor=selected_sensor, stage=stage)
def page_relay(): """ Display relay status and config """ camera = Camera.query.all() lcd = LCD.query.all() relay = Relay.query.all() user = User.query.all() conditional = Conditional.query.filter( Conditional.conditional_type == 'relay').all() conditional_actions = ConditionalActions.query.all() display_order = csv_to_list_of_int(DisplayOrder.query.first().relay) form_add_relay = flaskforms.RelayAdd() form_mod_relay = flaskforms.RelayMod() form_conditional = flaskforms.Conditional() form_conditional_actions = flaskforms.ConditionalActions() if request.method == 'POST': if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('general_routes.page_relay')) if form_add_relay.relay_add.data: flaskutils.relay_add(form_add_relay) elif form_mod_relay.save.data: flaskutils.relay_mod(form_mod_relay) elif (form_mod_relay.turn_on.data or form_mod_relay.turn_off.data or form_mod_relay.sec_on_submit.data): flaskutils.relay_on_off(form_mod_relay) elif form_mod_relay.delete.data: flaskutils.relay_del(form_mod_relay) elif form_mod_relay.order_up.data: flaskutils.relay_reorder(form_mod_relay.relay_id.data, display_order, 'up') elif form_mod_relay.order_down.data: flaskutils.relay_reorder(form_mod_relay.relay_id.data, display_order, 'down') elif form_conditional.add_cond.data: flaskutils.conditional_add(form_conditional.conditional_type.data, form_conditional.quantity.data) elif form_conditional.delete_cond.data: flaskutils.conditional_mod(form_conditional, 'delete') elif form_conditional.save_cond.data: flaskutils.conditional_mod(form_conditional, 'modify') elif form_conditional.activate_cond.data: flaskutils.conditional_activate(form_conditional) elif form_conditional.deactivate_cond.data: flaskutils.conditional_deactivate(form_conditional) elif form_conditional_actions.add_action.data: flaskutils.conditional_action_add(form_conditional_actions) elif form_conditional_actions.save_action.data: flaskutils.conditional_action_mod(form_conditional_actions, 'modify') elif form_conditional_actions.delete_action.data: flaskutils.conditional_action_mod(form_conditional_actions, 'delete') return redirect('/relay') return render_template('pages/relay.html', camera=camera, conditional=conditional, conditional_actions=conditional_actions, conditional_actions_list=CONDITIONAL_ACTIONS, displayOrder=display_order, form_conditional=form_conditional, form_conditional_actions=form_conditional_actions, form_add_relay=form_add_relay, form_mod_relay=form_mod_relay, lcd=lcd, relay=relay, user=user)
def admin_upgrade(): """ Display any available upgrades and option to upgrade """ if not flaskutils.user_has_permission('edit_settings'): return redirect(url_for('general_routes.home')) if not internet(): flash( gettext(u"Upgrade functionality is disabled because an internet " u"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(u"An upgrade is currently in progress. Please wait " u"for it to finish"), "error") elif upgrade == 2: flash( gettext(u"There was an error encountered during the upgrade " u"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 = [] try: maj_version = int(MYCODO_VERSION.split('.')[0]) releases = github_releases(maj_version) except Exception: flash( gettext(u"Could not determine local mycodo version or " u"online release versions"), "error") if len(releases): 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(u"The upgrade has started. The daemon will be " u"stopped during the upgrade."), "success") else: flash( gettext(u"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_info(): """ Display page with system information from command line tools """ if not flaskutils.user_has_permission('view_stats'): return redirect(url_for('general_routes.home')) uptime = subprocess.Popen("uptime", stdout=subprocess.PIPE, shell=True) (uptime_output, _) = uptime.communicate() uptime.wait() uname = subprocess.Popen("uname -a", stdout=subprocess.PIPE, shell=True) (uname_output, _) = uname.communicate() uname.wait() gpio = subprocess.Popen("gpio readall", stdout=subprocess.PIPE, shell=True) (gpio_output, _) = gpio.communicate() gpio.wait() df = subprocess.Popen("df -h", stdout=subprocess.PIPE, shell=True) (df_output, _) = df.communicate() df.wait() free = subprocess.Popen("free -h", stdout=subprocess.PIPE, shell=True) (free_output, _) = free.communicate() free.wait() ifconfig = subprocess.Popen("ifconfig -a", stdout=subprocess.PIPE, shell=True) (ifconfig_output, _) = ifconfig.communicate() ifconfig.wait() daemon_pid = None if os.path.exists(DAEMON_PID_FILE): with open(DAEMON_PID_FILE, 'r') as pid_file: daemon_pid = int(pid_file.read()) database_version = [] for each_ver in AlembicVersion.query.all(): database_version.append(each_ver.version_num) virtualenv_flask = False if hasattr(sys, 'real_prefix'): virtualenv_flask = True virtualenv_daemon = False pstree_output = None top_output = None daemon_up = daemon_active() if daemon_up: control = DaemonControl() ram_use_daemon = control.ram_use() virtualenv_daemon = control.is_in_virtualenv() pstree = subprocess.Popen("pstree -p {pid}".format(pid=daemon_pid), stdout=subprocess.PIPE, shell=True) (pstree_output, _) = pstree.communicate() pstree.wait() top = subprocess.Popen("top -bH -n 1 -p {pid}".format(pid=daemon_pid), stdout=subprocess.PIPE, shell=True) (top_output, _) = top.communicate() top.wait() else: ram_use_daemon = 0 ram_use_flask = resource.getrusage( resource.RUSAGE_SELF).ru_maxrss / float(1000) return render_template('pages/info.html', daemon_pid=daemon_pid, daemon_up=daemon_up, gpio_readall=gpio_output, database_version=database_version, df=df_output, free=free_output, ifconfig=ifconfig_output, pstree=pstree_output, ram_use_daemon=ram_use_daemon, ram_use_flask=ram_use_flask, top=top_output, uname=uname_output, uptime=uptime_output, virtualenv_daemon=virtualenv_daemon, virtualenv_flask=virtualenv_flask)
def page_graph(): """ Generate custom graphs to display sensor data retrieved from influxdb. """ # Create form objects form_mod_graph = flaskforms.GraphMod() form_del_graph = flaskforms.GraphDel() form_order_graph = flaskforms.GraphOrder() form_add_graph = flaskforms.GraphAdd() # Retrieve the order to display graphs display_order = csv_to_list_of_int(DisplayOrder.query.first().graph) # Retrieve tables from SQL database graph = Graph.query.all() pid = PID.query.all() relay = Relay.query.all() sensor = Sensor.query.all() # Retrieve all choices to populate form drop-down menu pid_choices = flaskutils.choices_pids(pid) output_choices = flaskutils.choices_outputs(relay) sensor_choices = flaskutils.choices_sensors(sensor) # Add multi-select values as form choices, for validation form_mod_graph.pid_ids.choices = [] form_mod_graph.relay_ids.choices = [] form_mod_graph.sensor_ids.choices = [] for key, value in pid_choices.items(): form_mod_graph.pid_ids.choices.append((key, value)) for key, value in output_choices.items(): form_mod_graph.relay_ids.choices.append((key, value)) for key, value in sensor_choices.items(): form_mod_graph.sensor_ids.choices.append((key, value)) # Generate dictionary of custom colors for each graph dict_colors = dict_custom_colors() # Detect which form on the page was submitted if request.method == 'POST': if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('general_routes.home')) form_name = request.form['form-name'] if form_name == 'modGraph': flaskutils.graph_mod(form_mod_graph, request.form) elif form_name == 'delGraph': flaskutils.graph_del(form_del_graph) elif form_order_graph.orderGraphUp.data: flaskutils.graph_reorder(form_order_graph.orderGraph_id.data, display_order, 'up') elif form_order_graph.orderGraphDown.data: flaskutils.graph_reorder(form_order_graph.orderGraph_id.data, display_order, 'down') elif form_name == 'addGraph': flaskutils.graph_add(form_add_graph, display_order) return redirect('/graph') return render_template('pages/graph.html', graph=graph, pid=pid, relay=relay, sensor=sensor, pid_choices=pid_choices, output_choices=output_choices, sensor_choices=sensor_choices, dict_colors=dict_colors, measurement_units=MEASUREMENT_UNITS, displayOrder=display_order, form_mod_graph=form_mod_graph, form_del_graph=form_del_graph, form_order_graph=form_order_graph, form_add_graph=form_add_graph)
def method_builder(method_id): """ Page to edit the details of each method This includes the (time, setpoint) data sets """ if not flaskutils.user_has_permission('edit_controllers'): return redirect(url_for('method_routes.method_list')) relay = Relay.query.all() form_create_method = flaskforms.MethodCreate() form_add_method = flaskforms.MethodAdd() form_mod_method = flaskforms.MethodMod() # Used in software tests to verify function is executing as admin if method_id == '-1': return 'admin logged in' # Create new method elif method_id == '0': form_fail = flaskutils.method_create(form_create_method) new_method = Method.query.order_by(Method.id.desc()).first() if not form_fail: return redirect( '/method-build/{method_id}'.format(method_id=new_method.id)) else: return redirect('/method') elif int(method_id) < 0: flash("Invalid method ID", "error") return redirect('/method') # First method column with general information about method method = Method.query.filter(Method.id == int(method_id)).first() if method.method_type in [ 'Date', 'Duration', 'Daily', 'DailySine', 'DailyBezier' ]: # Retrieve the order to display method data lines display_order = csv_to_list_of_int(method.method_order) method_data = MethodData.query.filter( MethodData.method_id == method.id) setpoint_method_data = MethodData.query.filter( MethodData.setpoint_start != None) sine_method_data = MethodData.query.filter( MethodData.amplitude != None) bezier_method_data = MethodData.query.filter(MethodData.x0 != None) if display_order is not None: last_setpoint_method = setpoint_method_data.filter( MethodData.id == display_order[-1]).first() last_sine_method = sine_method_data.filter( MethodData.id == display_order[-1]).first() last_bezier_method = bezier_method_data.filter( MethodData.id == display_order[-1]).first() else: last_setpoint_method = None last_sine_method = None last_bezier_method = None last_end_time = '' last_setpoint = '' if method.method_type in ['Daily', 'Date', 'Duration']: method_data = method_data.all() # Get last entry end time and setpoint to populate the form if last_setpoint_method is None: last_end_time = '' last_setpoint = '' else: last_end_time = last_setpoint_method.time_end if last_setpoint_method.setpoint_end is not None: last_setpoint = last_setpoint_method.setpoint_end else: last_setpoint = last_setpoint_method.setpoint_start if request.method == 'POST': form_name = request.form['form-name'] if form_name == 'addMethod': form_fail = flaskutils.method_add(form_add_method) elif form_name in ['modMethod', 'renameMethod']: form_fail = flaskutils.method_mod(form_mod_method) if (form_name in ['addMethod', 'modMethod', 'renameMethod'] and not form_fail): return redirect( '/method-build/{method_id}'.format(method_id=method.id)) if not method_data: method_data = [] return render_template('pages/method-build.html', method=method, relay=relay, method_data=method_data, method_id=method_id, last_end_time=last_end_time, last_bezier_method=last_bezier_method, last_sine_method=last_sine_method, last_setpoint_method=last_setpoint_method, last_setpoint=last_setpoint, form_create_method=form_create_method, form_add_method=form_add_method, form_mod_method=form_mod_method) return redirect('/method')