def settings_diagnostic(): """ Display general settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_settings_diagnostic = forms_settings.SettingsDiagnostic() if request.method == 'POST': if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) if form_settings_diagnostic.delete_dashboard_elements.data: utils_settings.settings_diagnostic_delete_dashboard_elements() elif form_settings_diagnostic.delete_inputs.data: utils_settings.settings_diagnostic_delete_inputs() elif form_settings_diagnostic.delete_maths.data: utils_settings.settings_diagnostic_delete_maths() elif form_settings_diagnostic.delete_notes_tags.data: utils_settings.settings_diagnostic_delete_notes_tags() elif form_settings_diagnostic.delete_outputs.data: utils_settings.settings_diagnostic_delete_outputs() elif form_settings_diagnostic.delete_settings_database.data: utils_settings.settings_diagnostic_delete_settings_database() elif form_settings_diagnostic.delete_file_dependency.data: utils_settings.settings_diagnostic_delete_file('dependency') elif form_settings_diagnostic.delete_file_upgrade.data: utils_settings.settings_diagnostic_delete_file('upgrade') elif form_settings_diagnostic.reset_email_counter.data: utils_settings.settings_diagnostic_reset_email_counter() return redirect(url_for('routes_settings.settings_diagnostic')) return render_template('settings/diagnostic.html', form_settings_diagnostic=form_settings_diagnostic)
def settings_users(): """ Display user settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) users = User.query.all() user_roles = Role.query.all() form_add_user = forms_settings.UserAdd() form_mod_user = forms_settings.UserMod() form_user_roles = forms_settings.UserRoles() if request.method == 'POST': if not utils_general.user_has_permission('edit_users'): return redirect(url_for('routes_general.home')) if form_add_user.add_user.data: utils_settings.user_add(form_add_user) elif form_mod_user.delete.data: if utils_settings.user_del(form_mod_user) == 'logout': return redirect('/logout') elif form_mod_user.save.data: if utils_settings.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): utils_settings.user_roles(form_user_roles) return redirect(url_for('routes_settings.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 settings_diagnostic(): """ Display general settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_settings_diagnostic = forms_settings.SettingsDiagnostic() if request.method == 'POST': if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) if form_settings_diagnostic.delete_dashboard_elements.data: utils_settings.settings_diagnostic_delete_dashboard_elements() elif form_settings_diagnostic.delete_inputs.data: utils_settings.settings_diagnostic_delete_inputs() elif form_settings_diagnostic.delete_maths.data: utils_settings.settings_diagnostic_delete_maths() elif form_settings_diagnostic.delete_notes_tags.data: utils_settings.settings_diagnostic_delete_notes_tags() elif form_settings_diagnostic.delete_outputs.data: utils_settings.settings_diagnostic_delete_outputs() elif form_settings_diagnostic.delete_settings_database.data: utils_settings.settings_diagnostic_delete_settings_database() elif form_settings_diagnostic.delete_file_dependency.data: utils_settings.settings_diagnostic_delete_file('dependency') elif form_settings_diagnostic.delete_file_upgrade.data: utils_settings.settings_diagnostic_delete_file('upgrade') return redirect(url_for('routes_settings.settings_diagnostic')) return render_template('settings/diagnostic.html', form_settings_diagnostic=form_settings_diagnostic)
def settings_pi(): """ Display general settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) misc = Misc.query.first() form_settings_pi = forms_settings.SettingsPi() pi_settings = get_raspi_config_settings() pigpiod_sample_rate = 999 if os.path.exists('/etc/systemd/system/pigpiod_low.service'): pigpiod_sample_rate = 1 elif os.path.exists('/etc/systemd/system/pigpiod_high.service'): pigpiod_sample_rate = 5 elif os.path.exists('/etc/systemd/system/pigpiod.service'): pigpiod_sample_rate = 1 if request.method == 'POST': if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) form_name = request.form['form-name'] if form_name == 'Pi': utils_settings.settings_pi_mod(form_settings_pi) return redirect(url_for('routes_settings.settings_pi')) return render_template('settings/pi.html', misc=misc, pi_settings=pi_settings, pigpiod_sample_rate=pigpiod_sample_rate, form_settings_pi=form_settings_pi)
def settings_input(): """ Display measurement settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_input = forms_settings.Input() form_input_delete = forms_settings.InputDel() dict_measurements = add_custom_measurements(Measurement.query.all()) dict_units = add_custom_units(Unit.query.all()) # Get list of custom inputs excluded_files = ['__init__.py', '__pycache__'] install_dir = os.path.abspath(INSTALL_DIRECTORY) path_custom_inputs = os.path.join(install_dir, 'mycodo/inputs/custom_inputs') if request.method == 'POST': if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) if form_input.import_input_upload.data: utils_settings.settings_input_import(form_input) elif form_input_delete.delete_input.data: utils_settings.settings_input_delete(form_input_delete) return redirect(url_for('routes_settings.settings_input')) dict_inputs = {} for each_file in os.listdir(path_custom_inputs): if each_file not in excluded_files: try: full_path_file = os.path.join(path_custom_inputs, each_file) input_info = load_module_from_file(full_path_file) dict_inputs[ input_info.INPUT_INFORMATION['input_name_unique']] = {} dict_inputs[input_info.INPUT_INFORMATION['input_name_unique']]['input_name'] = \ input_info.INPUT_INFORMATION['input_name'] dict_inputs[input_info.INPUT_INFORMATION['input_name_unique']]['input_manufacturer'] = \ input_info.INPUT_INFORMATION['input_manufacturer'] dict_inputs[input_info.INPUT_INFORMATION['input_name_unique']]['measurements_name'] = \ input_info.INPUT_INFORMATION['measurements_name'] except: pass # dict_inputs = parse_input_information() return render_template('settings/input.html', dict_inputs=dict_inputs, dict_measurements=dict_measurements, dict_units=dict_units, form_input=form_input, form_input_delete=form_input_delete)
def settings_input(): """ Display measurement settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_input = forms_settings.Input() form_input_delete = forms_settings.InputDel() dict_measurements = add_custom_measurements(Measurement.query.all()) dict_units = add_custom_units(Unit.query.all()) # Get list of custom inputs excluded_files = ['__init__.py', '__pycache__'] install_dir = os.path.abspath(INSTALL_DIRECTORY) path_custom_inputs = os.path.join(install_dir, 'mycodo/inputs/custom_inputs') if request.method == 'POST': if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) if form_input.import_input_upload.data: utils_settings.settings_input_import(form_input) elif form_input_delete.delete_input.data: utils_settings.settings_input_delete(form_input_delete) return redirect(url_for('routes_settings.settings_input')) dict_inputs = {} for each_file in os.listdir(path_custom_inputs): if each_file not in excluded_files: try: full_path_file = os.path.join(path_custom_inputs, each_file) input_info = load_module_from_file(full_path_file) dict_inputs[input_info.INPUT_INFORMATION['input_name_unique']] = {} dict_inputs[input_info.INPUT_INFORMATION['input_name_unique']]['input_name'] = \ input_info.INPUT_INFORMATION['input_name'] dict_inputs[input_info.INPUT_INFORMATION['input_name_unique']]['input_manufacturer'] = \ input_info.INPUT_INFORMATION['input_manufacturer'] dict_inputs[input_info.INPUT_INFORMATION['input_name_unique']]['measurements_name'] = \ input_info.INPUT_INFORMATION['measurements_name'] except: pass # dict_inputs = parse_input_information() return render_template('settings/input.html', dict_inputs=dict_inputs, dict_measurements=dict_measurements, dict_units=dict_units, form_input=form_input, form_input_delete=form_input_delete)
def test_123(): """ This endpoint will display different messages for logged in and logged out users. Be very careful when creating endpoints so unauthorized users don't have access to endpoints with sensitive information. """ if not current_user.is_authenticated: return "You are not logged in and cannot access this endpoint" user = User.query.filter(User.name == current_user.name).first() role = Role.query.filter(Role.id == user.role_id).first() return_message = "You are logged in as '{}' with the role '{}' and can access this endpoint".format( user.name, role.name) if user_has_permission('edit_settings'): return_message += "<br/>This user has permission to Edit Settings" if user_has_permission('edit_controllers'): return_message += "<br/>This user has permission to Edit Controllers" if user_has_permission('edit_users'): return_message += "<br/>This user has permission to Edit Users" if user_has_permission('view_settings'): return_message += "<br/>This user has permission to View Settings" if user_has_permission('view_camera'): return_message += "<br/>This user has permission to View Cameras" if user_has_permission('view_stats'): return_message += "<br/>This user has permission to View Stats" if user_has_permission('view_logs'): return_message += "<br/>This user has permission to View Logs" if user_has_permission('reset_password'): return_message += "<br/>This user has permission to Reset Passwords" return return_message
def change_theme(): """Change theme""" if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_prefs = forms_settings.UserPreferences() if request.method == 'POST': if not utils_general.user_has_permission('edit_users'): return redirect(url_for('routes_general.home')) if form_prefs.user_preferences_save.data: utils_settings.change_preferences(form_prefs) return redirect(url_for('routes_general.home'))
def settings_widget(): """Display widget settings.""" if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_widget = forms_settings.Widget() form_widget_delete = forms_settings.WidgetDel() dict_measurements = add_custom_measurements(Measurement.query.all()) dict_units = add_custom_units(Unit.query.all()) # Get list of custom widgets excluded_files = ['__init__.py', '__pycache__'] if request.method == 'POST': if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) if form_widget.import_widget_upload.data: utils_settings.settings_widget_import(form_widget) elif form_widget_delete.delete_widget.data: utils_settings.settings_widget_delete(form_widget_delete) return redirect(url_for('routes_settings.settings_widget')) dict_widgets = {} for each_file in os.listdir(PATH_WIDGETS_CUSTOM): if each_file not in excluded_files: try: full_path_file = os.path.join(PATH_WIDGETS_CUSTOM, each_file) widget_info, status = load_module_from_file( full_path_file, 'widgets') if widget_info: dict_widgets[widget_info.WIDGET_INFORMATION[ 'widget_name_unique']] = {} dict_widgets[widget_info.WIDGET_INFORMATION['widget_name_unique']]['widget_name'] = \ widget_info.WIDGET_INFORMATION['widget_name'] dict_widgets[widget_info.WIDGET_INFORMATION['widget_name_unique']]['measurements_name'] = \ widget_info.WIDGET_INFORMATION['measurements_name'] except: pass return render_template('settings/widget.html', dict_widgets=dict_widgets, dict_measurements=dict_measurements, dict_units=dict_units, form_widget=form_widget, form_widget_delete=form_widget_delete)
def settings_output(): """ Display measurement settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_output = forms_settings.Output() form_output_delete = forms_settings.OutputDel() dict_measurements = add_custom_measurements(Measurement.query.all()) dict_units = add_custom_units(Unit.query.all()) # Get list of custom outputs excluded_files = ['__init__.py', '__pycache__'] if request.method == 'POST': if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) if form_output.import_output_upload.data: utils_settings.settings_output_import(form_output) elif form_output_delete.delete_output.data: utils_settings.settings_output_delete(form_output_delete) return redirect(url_for('routes_settings.settings_output')) dict_outputs = {} for each_file in os.listdir(PATH_OUTPUTS_CUSTOM): if each_file not in excluded_files: try: full_path_file = os.path.join(PATH_OUTPUTS_CUSTOM, each_file) output_info = load_module_from_file(full_path_file, 'outputs') dict_outputs[ output_info.OUTPUT_INFORMATION['output_name_unique']] = {} dict_outputs[output_info.OUTPUT_INFORMATION['output_name_unique']]['output_name'] = \ output_info.OUTPUT_INFORMATION['output_name'] dict_outputs[output_info.OUTPUT_INFORMATION['output_name_unique']]['measurements_name'] = \ output_info.OUTPUT_INFORMATION['measurements_name'] except: pass # dict_outputs = parse_output_information() return render_template('settings/output.html', dict_outputs=dict_outputs, dict_measurements=dict_measurements, dict_units=dict_units, form_output=form_output, form_output_delete=form_output_delete)
def remote_input(): """Returns input information for remote administration""" if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.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 = [] host_auth = {} host_inputs = {} for each_host in remote_hosts: # Return input information about each host headers = remote_log_in(each_host.host, each_host.username, each_host.password_hash) _, host_inputs[each_host.host] = remote_host_page( each_host.host, headers, 'remote_get_inputs') host_inputs[each_host.host] = json.loads(host_inputs[each_host.host]) return render_template('remote/input.html', display_order=display_order, remote_hosts=remote_hosts, host_auth=host_auth, host_inputs=host_inputs)
def remote_setup(): """Return pages for remote administration""" if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.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 = [] form_setup = forms_authentication.RemoteSetup() if request.method == 'POST': if form_setup.add.data: utils_remote_host.remote_host_add(form_setup, display_order) elif form_setup.delete.data: utils_remote_host.remote_host_del(form_setup) return redirect('/remote/setup') host_auth = {} for each_host in remote_hosts: headers = remote_log_in(each_host.host, each_host.username, each_host.password_hash) _, host_auth[each_host.host] = remote_host_page( each_host.host, headers, 'auth') return render_template('remote/setup.html', form_setup=form_setup, display_order=display_order, remote_hosts=remote_hosts, host_auth=host_auth)
def post(self, unique_id, unit, channel, value): """Create a measurement""" if not utils_general.user_has_permission('edit_controllers'): abort(403) if channel < 0: abort(422, custom='channel must be >= 0') try: value = float(value) except: abort(422, custom='value does not represent a float') timestamp = None if ns_measurement.payload and 'timestamp' in ns_measurement.payload: ts = ns_measurement.payload["timestamp"] if ts is not None: if valid_date_str(ts): timestamp = datetime.datetime.strptime( ts, '%Y-%m-%dT%H:%M:%S.%fZ') else: abort(422, custom='Invalid timestamp format. Must be formatted as %Y-%m-%dT%H:%M:%S.%fZ') try: return_ = write_influxdb_value( unique_id, unit, value, channel=channel, timestamp=timestamp) if return_: abort(500) else: return {'message': 'Success'}, 200 except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def computer_command(action): """Execute one of several commands as root""" if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) try: if action not in [ 'restart', 'shutdown', 'daemon_restart', 'frontend_reload' ]: 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("System rebooting in 10 seconds"), "success") elif action == 'shutdown': flash(gettext("System shutting down in 10 seconds"), "success") elif action == 'daemon_restart': flash(gettext("Command to restart the daemon sent"), "success") elif action == 'frontend_reload': flash(gettext("Command to reload the frontend sent"), "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('routes_general.home'))
def get(self, unique_id): """Show the settings for an function""" if not utils_general.user_has_permission('view_settings'): abort(403) try: list_data = get_from_db(FunctionSchema, CustomController, unique_id=unique_id) function_channel_schema = FunctionChannelSchema() list_channels = return_list_of_dictionaries( function_channel_schema.dump( FunctionChannel.query.filter_by( function_id=unique_id).all(), many=True)) measure_schema = DeviceMeasurementsSchema() list_measurements = return_list_of_dictionaries( measure_schema.dump( DeviceMeasurements.query.filter_by( device_id=unique_id).all(), many=True)) return {'function settings': list_data, 'function channels': list_channels, 'device measurements': list_measurements}, 200 except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def get(self, unique_id): """Show the settings for an input.""" if not utils_general.user_has_permission('view_settings'): abort(403) try: list_data = get_from_db(InputSchema, Input, unique_id=unique_id) measure_schema = InputChannelSchema() list_channels = return_list_of_dictionaries( measure_schema.dump( InputChannel.query.filter_by(input_id=unique_id).all(), many=True)) measure_schema = DeviceMeasurementsSchema() list_measurements = return_list_of_dictionaries( measure_schema.dump(DeviceMeasurements.query.filter_by( device_id=unique_id).join(DeviceMeasurements.conversion, isouter=True).all(), many=True)) return { 'input settings': list_data, 'input channels': list_channels, 'device measurements': list_measurements }, 200 except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def page_dashboard_add(): """Add a dashboard""" if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) dashboard_id = utils_dashboard.dashboard_add() return redirect( url_for('routes_dashboard.page_dashboard', dashboard_id=dashboard_id))
def get(self): """Show all output settings and statuses""" if not utils_general.user_has_permission('view_settings'): abort(403) try: list_data = get_from_db(OutputSchema, Output) list_channels = get_from_db(OutputChannelSchema, OutputChannel) states = get_all_output_states() # Change integer channel keys to strings (flask-restx limitation?) new_state_dict = {} for each_id in states: new_state_dict[each_id] = {} for each_channel in states[each_id]: new_state_dict[each_id][str( each_channel)] = states[each_id][each_channel] if list_data: return { 'output devices': list_data, 'output channels': list_channels, 'output states': new_state_dict }, 200 except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def method_delete(method_id): """Delete a method""" action = '{action} {controller}'.format( action=TRANSLATIONS['delete']['title'], controller=gettext("Method")) if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_method.method_list')) try: MethodData.query.filter( MethodData.method_id == method_id).delete() MethodData.query.filter( MethodData.linked_method_id == method_id).delete() Method.query.filter( Method.unique_id == method_id).delete() display_order = csv_to_list_of_str(DisplayOrder.query.first().method) display_order.remove(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('routes_method.method_list'))
def remote_setup(): """Return pages for remote administration""" if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.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 = [] form_setup = forms_authentication.RemoteSetup() if request.method == 'POST': if form_setup.add.data: utils_remote_host.remote_host_add(form_setup, display_order) elif form_setup.delete.data: utils_remote_host.remote_host_del(form_setup) return redirect('/remote/setup') host_auth = {} for each_host in remote_hosts: headers = remote_log_in( each_host.host, each_host.username, each_host.password_hash) _, host_auth[each_host.host] = remote_host_page( each_host.host, headers, 'auth') return render_template('remote/setup.html', form_setup=form_setup, display_order=display_order, remote_hosts=remote_hosts, host_auth=host_auth)
def remote_input(): """Returns input information for remote administration""" if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.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 = [] host_auth = {} host_inputs = {} for each_host in remote_hosts: # Return input information about each host headers = remote_log_in( each_host.host, each_host.username, each_host.password_hash) _, host_inputs[each_host.host] = remote_host_page( each_host.host, headers, 'remote_get_inputs') host_inputs[each_host.host] = json.loads(host_inputs[each_host.host]) return render_template('remote/input.html', display_order=display_order, remote_hosts=remote_hosts, host_auth=host_auth, host_inputs=host_inputs)
def save_function_layout(): """Save positions of functions""" if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) data = request.get_json() keys = ('id', 'y') for each_function in data: if all(k in each_function for k in keys): controller_type = determine_controller_type(each_function['id']) if controller_type == "Conditional": mod_device = Conditional.query.filter( Conditional.unique_id == each_function['id']).first() elif controller_type == "PID": mod_device = PID.query.filter( PID.unique_id == each_function['id']).first() elif controller_type == "Trigger": mod_device = Trigger.query.filter( Trigger.unique_id == each_function['id']).first() elif controller_type == "Function": mod_device = Function.query.filter( Function.unique_id == each_function['id']).first() elif controller_type == "Function_Custom": mod_device = CustomController.query.filter( CustomController.unique_id == each_function['id']).first() else: logger.info("Could not find controller with ID {}".format( each_function['id'])) return "error" if mod_device: mod_device.position_y = each_function['y'] db.session.commit() return "success"
def get(self, unique_id, unit, channel, past_seconds): """ Return the last measurement found within a duration from the past to the present """ if not utils_general.user_has_permission('view_settings'): abort(403) if unit not in add_custom_units(Unit.query.all()): abort(422, custom='Unit ID not found') if channel < 0: abort(422, custom='channel must be >= 0') if past_seconds < 1: abort(422, custom='past_seconds must be >= 1') try: return_ = read_influxdb_single(unique_id, unit, channel, duration_sec=past_seconds) if return_ and len(return_) == 2: return {'time': return_[0], 'value': return_[1]}, 200 else: return return_, 200 except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def output_mod(output_id, channel, state, output_type, amount): """ Manipulate output (using non-unique ID) """ if not utils_general.user_has_permission('edit_controllers'): return 'Insufficient user permissions to manipulate outputs' if is_int(channel): # if an integer was returned output_channel = int(channel) else: # if a channel ID was returned channel_dev = db_retrieve_table(OutputChannel).filter( OutputChannel.unique_id == channel).first() if channel_dev: output_channel = channel_dev.channel else: return "Could not determine channel number from channel ID '{}'".format( channel) daemon = DaemonControl() if (state in ['on', 'off'] and str_is_float(amount) and ((output_type in ['sec', 'pwm', 'vol'] and float(amount) >= 0) or (output_type == 'value'))): out_status = daemon.output_on_off(output_id, state, output_type=output_type, amount=float(amount), output_channel=output_channel) if out_status[0]: return 'ERROR: {}'.format(out_status[1]) else: return 'SUCCESS: {}'.format(out_status[1]) else: return 'ERROR: unknown parameters: ' \ 'output_id: {}, channel: {}, state: {}, output_type: {}, amount: {}'.format( output_id, channel, state, output_type, amount)
def computer_command(action): """Execute one of several commands as root""" if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) try: if action not in ['restart', 'shutdown', 'daemon_restart', 'frontend_reload']: 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("System rebooting in 10 seconds"), "success") elif action == 'shutdown': flash(gettext("System shutting down in 10 seconds"), "success") elif action == 'daemon_restart': flash(gettext("Command to restart the daemon sent"), "success") elif action == 'frontend_reload': flash(gettext("Command to reload the frontend sent"), "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('routes_general.home'))
def get(self, unique_id, unit, channel, past_seconds): """ Return a list of measurements found within a duration from the past to the present """ if not utils_general.user_has_permission('view_settings'): abort(403) if unit not in add_custom_units(Unit.query.all()): abort(422, custom='Unit ID not found') if channel < 0: abort(422, custom='channel must be >= 0') if past_seconds < 1: abort(422, custom='past_seconds must be >= 1') try: return_ = read_influxdb_list(unique_id, unit, channel, duration_sec=past_seconds) if return_ and len(return_) > 0: dict_return = {'measurements': []} for each_set in return_: dict_return['measurements'].append({ 'time': each_set[0], 'value': each_set[1] }) return dict_return, 200 else: return return_, 200 except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def get(self, unique_id): """Show the settings and status for an output.""" if not utils_general.user_has_permission('edit_controllers'): abort(403) try: list_data = get_from_db(OutputSchema, Output, unique_id=unique_id) output_channel_schema = OutputChannelSchema() list_channels = return_list_of_dictionaries( output_channel_schema.dump( OutputChannel.query.filter_by( output_id=unique_id).all(), many=True)) states = get_all_output_states() # Change integer channel keys to strings (flask-restx limitation?) new_state_dict = {} for each_channel in states[unique_id]: new_state_dict[str(each_channel)] = states[unique_id][each_channel] return {'output device': list_data, 'output device channels': list_channels, 'output device channel states': new_state_dict}, 200 except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def get(self, unique_id): """Show the settings and status for an output""" if not utils_general.user_has_permission('edit_controllers'): abort(403) try: dict_data = get_from_db(OutputSchema, Output, unique_id=unique_id) measure_schema = DeviceMeasurementsSchema() list_data = return_list_of_dictionaries( measure_schema.dump(DeviceMeasurements.query.filter_by( device_id=unique_id).all(), many=True)) control = DaemonControl() output_state = control.output_state(unique_id) return { 'output settings': dict_data, 'output device measurements': list_data, 'output state': output_state }, 200 except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def output_mod(output_id, channel_id, state, output_type, amount): """ Manipulate output (using non-unique ID) """ if not utils_general.user_has_permission('edit_controllers'): return 'Insufficient user permissions to manipulate outputs' if channel_id == '0': # some parts of pages don't have access to the channel ID and only know there is 1 channel channel = db_retrieve_table(OutputChannel).filter(and_( OutputChannel.output_id == output_id, OutputChannel.channel == 0)).first() else: channel = db_retrieve_table(OutputChannel, unique_id=channel_id) daemon = DaemonControl() if (state in ['on', 'off'] and output_type in ['sec', 'pwm', 'vol'] and (str_is_float(amount) and float(amount) >= 0)): out_status = daemon.output_on_off( output_id, state, output_type=output_type, amount=float(amount), output_channel=channel.channel) if out_status[0]: return 'ERROR: {}'.format(out_status[1]) else: return 'SUCCESS: {}'.format(out_status[1])
def post(self, unique_id): """Change the state of an output""" if not utils_general.user_has_permission('edit_controllers'): abort(403) control = DaemonControl() state = None duration = None duty_cycle = None if ns_output.payload: if 'state' in ns_output.payload: state = ns_output.payload["state"] if state is not None: try: state = bool(state) except Exception: abort(422, message='state must represent a bool value') if 'duration' in ns_output.payload: duration = ns_output.payload["duration"] if duration is not None: try: duration = float(duration) except Exception: abort(422, message='duration does not represent a number') else: duration = 0 if 'duty_cycle' in ns_output.payload: duty_cycle = ns_output.payload["duty_cycle"] if duty_cycle is not None: try: duty_cycle = float(duty_cycle) if duty_cycle < 0 or duty_cycle > 100: abort(422, message='Required: 0 <= duty_cycle <= 100') except Exception: abort(422, message='duty_cycle does not represent float value') try: if state is not None and duration is not None: return_ = control.output_on_off( unique_id, state, amount=duration) elif state is not None: return_ = control.output_on_off(unique_id, state) elif duty_cycle is not None: return_ = control.output_duty_cycle( unique_id, duty_cycle=duty_cycle) else: return {'message': 'Insufficient payload'}, 460 return return_handler(return_) except Exception: abort(500, message='An exception occurred', error=traceback.format_exc())
def settings_controller(): """ Display controller settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_controller = forms_settings.Controller() form_controller_delete = forms_settings.ControllerDel() # Get list of custom controllers excluded_files = ['__init__.py', '__pycache__'] if request.method == 'POST': if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) if form_controller.import_controller_upload.data: utils_settings.settings_controller_import(form_controller) elif form_controller_delete.delete_controller.data: utils_settings.settings_controller_delete(form_controller_delete) return redirect(url_for('routes_settings.settings_controller')) dict_controllers = {} for each_file in os.listdir(PATH_CONTROLLERS_CUSTOM): if each_file not in excluded_files: try: full_path_file = os.path.join(PATH_CONTROLLERS_CUSTOM, each_file) controller_info = load_module_from_file( full_path_file, 'controllers') dict_controllers[controller_info.CONTROLLER_INFORMATION[ 'controller_name_unique']] = {} dict_controllers[controller_info.CONTROLLER_INFORMATION['controller_name_unique']]['controller_name'] = \ controller_info.CONTROLLER_INFORMATION['controller_name'] except: pass # dict_controllers = parse_controller_information() return render_template('settings/controller.html', dict_controllers=dict_controllers, form_controller=form_controller, form_controller_delete=form_controller_delete)
def page_output_submit(): """Submit form for Output page""" messages = {"success": [], "info": [], "warning": [], "error": []} page_refresh = False output_id = None dep_unmet = '' dep_name = '' dep_list = [] dep_message = '' size_y = None form_add_output = forms_output.OutputAdd() form_mod_output = forms_output.OutputMod() if not utils_general.user_has_permission('edit_controllers'): messages["error"].append("Your permissions do not allow this action") if not messages["error"]: if form_add_output.output_add.data: (messages, dep_name, dep_list, dep_message, output_id, size_y) = utils_output.output_add(form_add_output, request.form) if dep_list: dep_unmet = form_add_output.output_type.data.split(',')[0] elif form_mod_output.output_mod.data: messages, page_refresh = utils_output.output_mod( form_mod_output, request.form) output_id = form_mod_output.output_id.data elif form_mod_output.output_delete.data: messages = utils_output.output_del(form_mod_output) output_id = form_mod_output.output_id.data # Custom action else: custom_button = False for key in request.form.keys(): if key.startswith('custom_button_'): custom_button = True break if custom_button: messages = utils_general.custom_command( "Output", parse_output_information(), form_mod_output.output_id.data, request.form) else: messages["error"].append("Unknown output directive") return jsonify( data={ 'output_id': output_id, 'dep_name': dep_name, 'dep_list': dep_list, 'dep_unmet': dep_unmet, 'dep_message': dep_message, 'size_y': size_y, 'messages': messages, "page_refresh": page_refresh })
def pid_mod_unique_id(unique_id, state): """ Manipulate output (using unique ID) """ if not utils_general.user_has_permission('edit_controllers'): return 'Insufficient user permissions to manipulate PID' pid = PID.query.filter(PID.unique_id == unique_id).first() daemon = DaemonControl() if state == 'activate_pid': pid.is_activated = True pid.save() return_val, return_str = daemon.controller_activate( 'PID', pid.unique_id) return return_str elif state == 'deactivate_pid': pid.is_activated = False pid.is_paused = False pid.is_held = False pid.save() return_val, return_str = daemon.controller_deactivate( 'PID', pid.unique_id) return return_str elif state == 'pause_pid': pid.is_paused = True pid.save() if pid.is_activated: return_str = daemon.pid_pause(pid.unique_id) else: return_str = "PID Paused (Note: PID is not currently active)" return return_str elif state == 'hold_pid': pid.is_held = True pid.save() if pid.is_activated: return_str = daemon.pid_hold(pid.unique_id) else: return_str = "PID Held (Note: PID is not currently active)" return return_str elif state == 'resume_pid': pid.is_held = False pid.is_paused = False pid.save() if pid.is_activated: return_str = daemon.pid_resume(pid.unique_id) else: return_str = "PID Resumed (Note: PID is not currently active)" return return_str elif 'set_setpoint_pid' in state: pid.setpoint = state.split('|')[1] pid.save() if pid.is_activated: return_str = daemon.pid_set(pid.unique_id, 'setpoint', float(state.split('|')[1])) else: return_str = "PID Setpoint changed (Note: PID is not currently active)" return return_str
def settings_users(): """Display user settings.""" if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) messages = {"success": [], "info": [], "warning": [], "error": []} misc = Misc.query.first() form_user = forms_settings.User() form_add_user = forms_settings.UserAdd() form_mod_user = forms_settings.UserMod() form_user_roles = forms_settings.UserRoles() if request.method == 'POST': if not utils_general.user_has_permission('edit_users'): return redirect(url_for('routes_general.home')) if form_add_user.user_add.data: utils_settings.user_add(form_add_user) elif form_user_roles.user_role_add.data: messages, page_refresh = utils_settings.user_roles(form_user_roles) for each_error in messages["error"]: flash(each_error, "error") for each_warn in messages["warning"]: flash(each_warn, "warning") for each_info in messages["info"]: flash(each_info, "info") for each_success in messages["success"]: flash(each_success, "success") users = User.query.all() user_roles = Role.query.all() return render_template('settings/users.html', misc=misc, themes=THEMES, users=users, user_roles=user_roles, form_add_user=form_add_user, form_mod_user=form_mod_user, form_user=form_user, form_user_roles=form_user_roles)
def admin_statistics(): """ Display collected statistics """ if not utils_general.user_has_permission('view_stats'): return redirect(url_for('routes_general.home')) try: statistics = return_stat_file_dict(STATS_CSV) except IOError: statistics = {} return render_template('admin/statistics.html', statistics=statistics)
def settings_alerts(): """ Display alert settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) smtp = SMTP.query.first() form_email_alert = forms_settings.SettingsEmail() if request.method == 'POST': if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) form_name = request.form['form-name'] if form_name == 'EmailAlert': utils_settings.settings_alert_mod(form_email_alert) return redirect(url_for('routes_settings.settings_alerts')) return render_template('settings/alerts.html', smtp=smtp, form_email_alert=form_email_alert)
def pid_mod_unique_id(unique_id, state): """ Manipulate output (using unique ID) """ if not utils_general.user_has_permission('edit_controllers'): return 'Insufficient user permissions to manipulate PID' pid = PID.query.filter(PID.unique_id == unique_id).first() daemon = DaemonControl() if state == 'activate_pid': pid.is_activated = True pid.save() return_val, return_str = daemon.controller_activate('PID', pid.unique_id) return return_str elif state == 'deactivate_pid': pid.is_activated = False pid.is_paused = False pid.is_held = False pid.save() return_val, return_str = daemon.controller_deactivate('PID', pid.unique_id) return return_str elif state == 'pause_pid': pid.is_paused = True pid.save() if pid.is_activated: return_str = daemon.pid_pause(pid.unique_id) else: return_str = "PID Paused (Note: PID is not currently active)" return return_str elif state == 'hold_pid': pid.is_held = True pid.save() if pid.is_activated: return_str = daemon.pid_hold(pid.unique_id) else: return_str = "PID Held (Note: PID is not currently active)" return return_str elif state == 'resume_pid': pid.is_held = False pid.is_paused = False pid.save() if pid.is_activated: return_str = daemon.pid_resume(pid.unique_id) else: return_str = "PID Resumed (Note: PID is not currently active)" return return_str elif 'set_setpoint_pid' in state: pid.setpoint = state.split('|')[1] pid.save() if pid.is_activated: return_str = daemon.pid_set(pid.unique_id, 'setpoint', float(state.split('|')[1])) else: return_str = "PID Setpoint changed (Note: PID is not currently active)" return return_str
def output_mod(output_id, state, out_type, amount): """ Manipulate output (using non-unique ID) """ if not utils_general.user_has_permission('edit_controllers'): return 'Insufficient user permissions to manipulate outputs' daemon = DaemonControl() if (state in ['on', 'off'] and out_type == 'sec' and (str_is_float(amount) and float(amount) >= 0)): return daemon.output_on_off(output_id, state, float(amount)) elif (state == 'on' and out_type in ['pwm', 'command_pwm'] and (str_is_float(amount) and float(amount) >= 0)): return daemon.output_on(output_id, duty_cycle=float(amount))
def settings_general(): """ Display general settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) misc = Misc.query.first() form_settings_general = forms_settings.SettingsGeneral() languages_sorted = sorted(LANGUAGES.items(), key=operator.itemgetter(1)) if request.method == 'POST': if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) form_name = request.form['form-name'] if form_name == 'General': utils_settings.settings_general_mod(form_settings_general) return redirect(url_for('routes_settings.settings_general')) return render_template('settings/general.html', misc=misc, languages=languages_sorted, form_settings_general=form_settings_general)
def settings_pi(): """ Display general settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) misc = Misc.query.first() form_settings_pi = forms_settings.SettingsPi() pi_settings = get_raspi_config_settings() # Determine what state pigpiod is currently in pigpiod_sample_rate = '' if os.path.exists('/etc/systemd/system/pigpiod_uninstalled.service'): pigpiod_sample_rate = 'uninstalled' elif os.path.exists('/etc/systemd/system/pigpiod_disabled.service'): pigpiod_sample_rate = 'disabled' elif os.path.exists('/etc/systemd/system/pigpiod_low.service'): pigpiod_sample_rate = 'low' elif os.path.exists('/etc/systemd/system/pigpiod_high.service'): pigpiod_sample_rate = 'high' elif os.path.exists('/etc/systemd/system/pigpiod.service'): pigpiod_sample_rate = 'low' if request.method == 'POST': if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) form_name = request.form['form-name'] if form_name == 'Pi': utils_settings.settings_pi_mod(form_settings_pi) return redirect(url_for('routes_settings.settings_pi')) return render_template('settings/pi.html', misc=misc, pi_settings=pi_settings, pigpiod_sample_rate=pigpiod_sample_rate, form_settings_pi=form_settings_pi)
def settings_camera(): """ Display camera settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) form_camera = forms_settings.SettingsCamera() camera = Camera.query.all() output = Output.query.all() 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 utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) if form_camera.camera_add.data: utils_settings.camera_add(form_camera) elif form_camera.camera_mod.data: utils_settings.camera_mod(form_camera) elif form_camera.camera_del.data: utils_settings.camera_del(form_camera) return redirect(url_for('routes_settings.settings_camera')) return render_template('settings/camera.html', camera=camera, camera_libraries=CAMERA_LIBRARIES, form_camera=form_camera, pi_camera_enabled=pi_camera_enabled, output=output)
def admin_dependencies(device): """ Display Dependency page """ form_dependencies = forms_dependencies.Dependencies() if device != '0': device_unmet_dependencies, _ = utils_general.return_dependencies(device) elif form_dependencies.device.data: device_unmet_dependencies, _ = utils_general.return_dependencies(form_dependencies.device.data) else: device_unmet_dependencies = [] unmet_dependencies = OrderedDict() unmet_exist = False met_dependencies = [] met_exist = False unmet_list = {} install_in_progress = False device_name = None # Read from the dependency status file created by the upgrade script # to indicate if the upgrade is running. try: with open(DEPENDENCY_INIT_FILE) as f: dep = int(f.read(1)) except (IOError, ValueError): try: with open(DEPENDENCY_INIT_FILE, 'w') as f: f.write('0') finally: dep = 0 if dep: install_in_progress = True dict_inputs = parse_input_information() list_dependencies = [ dict_inputs, LCD_INFO, MATH_INFO, METHOD_INFO, OUTPUT_INFO, CALIBRATION_INFO ] for each_section in list_dependencies: for each_device in each_section: if device in each_section: for each_device, each_val in each_section[device].items(): if each_device in ['name', 'input_name']: device_name = each_val # Determine if there are any unmet dependencies dep_unmet, dep_met = utils_general.return_dependencies(each_device) unmet_dependencies.update({ each_device: dep_unmet }) if dep_unmet: unmet_exist = True # Determine if there are any met dependencies if dep_met: if each_device not in met_dependencies: met_dependencies.append(each_device) met_exist = True # Find all the devices that use each unmet dependency if unmet_dependencies[each_device]: for each_dep in unmet_dependencies[each_device]: if each_dep not in unmet_list: unmet_list[each_dep] = [] if each_device not in unmet_list[each_dep]: unmet_list[each_dep].append(each_device) if request.method == 'POST': if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_admin.admin_dependencies', device=device)) if form_dependencies.install.data: install_in_progress = True with open(DEPENDENCY_INIT_FILE, 'w') as f: f.write('1') install_deps = threading.Thread( target=install_dependencies, args=(device_unmet_dependencies,)) install_deps.start() return redirect(url_for('routes_admin.admin_dependencies', device=device)) return render_template('admin/dependencies.html', measurements=parse_input_information(), unmet_list=unmet_list, device=device, device_name=device_name, install_in_progress=install_in_progress, unmet_dependencies=unmet_dependencies, unmet_exist=unmet_exist, met_dependencies=met_dependencies, met_exist=met_exist, form_dependencies=form_dependencies, device_unmet_dependencies=device_unmet_dependencies)
def admin_upgrade(): """ Display any available upgrades and option to upgrade """ if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.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(UPGRADE_INIT_FILE) as f: upgrade = int(f.read(1)) except IOError: try: with open(UPGRADE_INIT_FILE, 'w') as f: f.write('0') finally: upgrade = 0 if upgrade: if 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 = forms_misc.Backup() form_upgrade = forms_misc.Upgrade() is_internet = True upgrade_available = False # Check for any new Mycodo releases on github releases = [] try: current_maj_version = int(MYCODO_VERSION.split('.')[0]) releases = github_releases(current_maj_version) except Exception as err: flash(gettext("Could not determine local mycodo version or " "online release versions: {err}".format(err=err)), "error") if len(releases): current_latest_release = github_latest_release() current_latest_major_version = current_latest_release.split('.')[0] current_major_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) or parse_version(current_latest_release[0]) > parse_version(MYCODO_VERSION)): upgrade_available = True else: current_releases = [] current_latest_release = '0.0.0' current_latest_major_version = '0' current_major_release = '0.0.0' releases_behind = 0 # Update database to reflect the current upgrade status mod_misc = Misc.query.first() if mod_misc.mycodo_upgrade_available != upgrade_available: mod_misc.mycodo_upgrade_available = upgrade_available db.session.commit() def not_enough_space_upgrade(): backup_size, free_before, free_after = can_perform_backup() if free_after / 1000000 < 50: flash( "A backup must be performed during an upgrade and there is " "not enough free space to perform a backup. A backup " "requires {size_bu:.1f} MB but there is only {size_free:.1f} " "MB available, which would leave {size_after:.1f} MB after " "the backup. If the free space after a backup is less than 50" " MB, the backup cannot proceed. Free up space by deleting " "current backups.".format(size_bu=backup_size / 1000000, size_free=free_before / 1000000, size_after=free_after / 1000000), 'error') return True else: return False if request.method == 'POST': if (form_upgrade.upgrade.data and (upgrade_available or FORCE_UPGRADE_MASTER)): if not_enough_space_upgrade(): pass elif FORCE_UPGRADE_MASTER: cmd = "{pth}/mycodo/scripts/mycodo_wrapper upgrade-master" \ " | ts '[%Y-%m-%d %H:%M:%S]'" \ " >> {log} 2>&1".format(pth=INSTALL_DIRECTORY, log=UPGRADE_LOG_FILE) subprocess.Popen(cmd, shell=True) upgrade = 1 flash(gettext("The upgrade (from master branch) has started"), "success") else: cmd = "{pth}/mycodo/scripts/mycodo_wrapper upgrade" \ " | ts '[%Y-%m-%d %H:%M:%S]'" \ " >> {log} 2>&1".format(pth=INSTALL_DIRECTORY, log=UPGRADE_LOG_FILE) subprocess.Popen(cmd, shell=True) upgrade = 1 mod_misc = Misc.query.first() mod_misc.mycodo_upgrade_available = False db.session.commit() flash(gettext("The upgrade has started"), "success") elif (form_upgrade.upgrade_next_major_version.data and upgrade_available): if not not_enough_space_upgrade(): cmd = "{pth}/mycodo/scripts/mycodo_wrapper upgrade-release-major {ver}" \ " | ts '[%Y-%m-%d %H:%M:%S]'" \ " >> {log} 2>&1".format(pth=INSTALL_DIRECTORY, ver=current_latest_major_version, log=UPGRADE_LOG_FILE) subprocess.Popen(cmd, shell=True) upgrade = 1 mod_misc = Misc.query.first() mod_misc.mycodo_upgrade_available = False db.session.commit() flash(gettext("The major version upgrade has started"), "success") else: flash(gettext("You cannot upgrade if an upgrade is not available"), "error") return render_template('admin/upgrade.html', final_releases=FINAL_RELEASES, force_upgrade_master=FORCE_UPGRADE_MASTER, form_backup=form_backup, form_upgrade=form_upgrade, current_release=MYCODO_VERSION, current_releases=current_releases, current_major_release=current_major_release, current_latest_release=current_latest_release, current_latest_major_version=current_latest_major_version, releases_behind=releases_behind, upgrade_available=upgrade_available, upgrade=upgrade, is_internet=is_internet)
def admin_backup(): """ Load the backup management page """ if not utils_general.user_has_permission('edit_settings'): return redirect(url_for('routes_general.home')) form_backup = forms_misc.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-"): full_path = os.path.join(BACKUP_PATH, each_dir) backup_dirs.append((each_dir, get_directory_size(full_path) / 1000000.0)) full_paths.append(full_path) if request.method == 'POST': if form_backup.backup.data: backup_size, free_before, free_after = can_perform_backup() if free_after / 1000000 > 50: cmd = "{pth}/mycodo/scripts/mycodo_wrapper backup-create" \ " | ts '[%Y-%m-%d %H:%M:%S]'" \ " >> {log} 2>&1".format(pth=INSTALL_DIRECTORY, log=BACKUP_LOG_FILE) subprocess.Popen(cmd, shell=True) flash(gettext("Backup in progress"), "success") else: flash( "Not enough free space to perform a backup. A backup " "requires {size_bu:.1f} MB but there is only " "{size_free:.1f} MB available, which would leave " "{size_after:.1f} MB after the backup. If the free space " "after a backup is less than 50 MB, the backup cannot " "proceed. Free up space by deleting current " "backups.".format(size_bu=backup_size / 1000000, size_free=free_before / 1000000, size_after=free_after / 1000000), 'error') 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("Deletion of backup in progress"), "success") elif form_backup.restore.data: cmd = "{pth}/mycodo/scripts/mycodo_wrapper backup-restore {backup}" \ " | ts '[%Y-%m-%d %H:%M:%S]'" \ " >> {log} 2>&1".format(pth=INSTALL_DIRECTORY, backup=form_backup.full_path.data, log=RESTORE_LOG_FILE) subprocess.Popen(cmd, shell=True) flash(gettext("Restore in progress"), "success") return render_template('admin/backup.html', form_backup=form_backup, backup_dirs=backup_dirs, full_paths=full_paths)
def settings_measurement(): """ Display measurement settings """ if not utils_general.user_has_permission('view_settings'): return redirect(url_for('routes_general.home')) measurement = Measurement.query.all() unit = Unit.query.all() conversion = Conversion.query.all() form_add_measurement = forms_settings.MeasurementAdd() form_mod_measurement = forms_settings.MeasurementMod() form_add_unit = forms_settings.UnitAdd() form_mod_unit = forms_settings.UnitMod() form_add_conversion = forms_settings.ConversionAdd() form_mod_conversion = forms_settings.ConversionMod() choices_units = utils_general.choices_units(unit) # Generate all measurement and units used dict_measurements = add_custom_measurements(measurement) dict_units = add_custom_units(unit) if request.method == 'POST': if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) if form_add_measurement.add_measurement.data: utils_settings.settings_measurement_add(form_add_measurement) elif form_mod_measurement.save_measurement.data: utils_settings.settings_measurement_mod(form_mod_measurement) elif form_mod_measurement.delete_measurement.data: utils_settings.settings_measurement_del(form_mod_measurement.measurement_id.data) elif form_add_unit.add_unit.data: utils_settings.settings_unit_add(form_add_unit) elif form_mod_unit.save_unit.data: utils_settings.settings_unit_mod(form_mod_unit) elif form_mod_unit.delete_unit.data: utils_settings.settings_unit_del(form_mod_unit.unit_id.data) elif form_add_conversion.add_conversion.data: utils_settings.settings_convert_add(form_add_conversion) elif form_mod_conversion.save_conversion.data: utils_settings.settings_convert_mod(form_mod_conversion) elif form_mod_conversion.delete_conversion.data: utils_settings.settings_convert_del(form_mod_conversion.conversion_id.data) return redirect(url_for('routes_settings.settings_measurement')) return render_template('settings/measurement.html', dict_measurements=dict_measurements, dict_units=dict_units, choices_units=choices_units, measurement=measurement, unit=unit, conversion=conversion, form_add_measurement=form_add_measurement, form_mod_measurement=form_mod_measurement, form_add_unit=form_add_unit, form_mod_unit=form_mod_unit, form_add_conversion=form_add_conversion, form_mod_conversion=form_mod_conversion)