def __init__(self, ready, unique_id, testing=False): threading.Thread.__init__(self) super(CustomModule, self).__init__(ready, unique_id=unique_id, name=__name__) self.unique_id = unique_id self.log_level_debug = None self.timer_loop = time.time() self.control = DaemonControl() # Initialize custom options self.period = None self.start_offset = None self.select_measurement = None self.max_measure_age = None # Set custom options custom_function = db_retrieve_table_daemon(CustomController, unique_id=unique_id) self.setup_custom_options(FUNCTION_INFORMATION['custom_options'], custom_function)
def action_setpoint_pid(cond_action, message): control = DaemonControl() pid = db_retrieve_table_daemon(PID, unique_id=cond_action.do_unique_id, entry='first') message += " Set Setpoint of PID {unique_id} ({id}, {name}).".format( unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name) if pid.is_activated: setpoint_pid = threading.Thread(target=control.pid_set, args=( pid.unique_id, 'setpoint', float( cond_action.do_action_string), )) setpoint_pid.start() else: with session_scope(MYCODO_DB_PATH) as new_session: mod_pid = new_session.query(PID).filter( PID.unique_id == cond_action.do_unique_id).first() mod_pid.setpoint = float(cond_action.do_action_string) new_session.commit() return message
def gpio_state_unique_id(unique_id): """Return the GPIO state, for dashboard output """ output = Output.query.filter( Output.unique_id == unique_id).first() daemon_control = DaemonControl() GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) if output.relay_type == 'wired' and output.pin and -1 < output.pin < 40: GPIO.setup(output.pin, GPIO.OUT) if GPIO.input(output.pin) == output.trigger: state = 'on' else: state = 'off' elif (output.relay_type in ['command', 'command_pwm'] or (output.relay_type in ['pwm', 'wireless_433MHz_pi_switch'] and output.pin and -1 < output.pin < 40)): state = daemon_control.relay_state(output.id) else: state = None return jsonify(state)
def output_mod(output_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' daemon = DaemonControl() if (state in ['on', 'off'] and output_type in ['sec', '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)) if out_status[0]: return 'ERROR: {}'.format(out_status[1]) else: return 'SUCCESS: {}'.format(out_status[1]) elif (state == 'on' and output_type in outputs_pwm() and (str_is_float(amount) and float(amount) >= 0)): out_status = daemon.output_on( output_id, output_type=output_type, duty_cycle=float(amount)) if out_status[0]: return 'ERROR: {}'.format(out_status[1]) else: return 'SUCCESS: {}'.format(out_status[1])
def gpio_state(): """Return the GPIO state, for output page status""" output = Output.query.all() daemon_control = DaemonControl() state = {} GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) for each_output in output: if each_output.relay_type == 'wired' and each_output.pin and -1 < each_output.pin < 40: GPIO.setup(each_output.pin, GPIO.OUT) if GPIO.input(each_output.pin) == each_output.trigger: state[each_output.id] = 'on' else: state[each_output.id] = 'off' elif (each_output.relay_type in ['command', 'command_pwm'] or (each_output.relay_type in ['pwm', 'wireless_433MHz_pi_switch'] and each_output.pin and -1 < each_output.pin < 40)): state[each_output.id] = daemon_control.relay_state(each_output.id) else: state[each_output.id] = None return jsonify(state)
def action_deactivate_controller(cond_action, message): control = DaemonControl() (controller_type, controller_object, controller_entry) = which_controller( cond_action.do_unique_id) message += " Deactivate Controller {unique_id} ({id}, {name}).".format( unique_id=cond_action.do_unique_id, id=controller_entry.id, name=controller_entry.name) if not controller_entry.is_activated: message += " Notice: Controller is already inactive!" else: with session_scope(MYCODO_DB_PATH) as new_session: mod_cont = new_session.query(controller_object).filter( controller_object.unique_id == cond_action.do_unique_id).first() mod_cont.is_activated = False new_session.commit() deactivate_controller = threading.Thread( target=control.controller_deactivate, args=(cond_action.do_unique_id,)) deactivate_controller.start() return message
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 __init__(self): threading.Thread.__init__(self) self.logger = logging.getLogger("mycodo.conditional") self.running = False self.thread_startup_timer = timeit.default_timer() self.thread_shutdown_timer = 0 self.pause_loop = False self.verify_pause_loop = True self.control = DaemonControl() self.is_activated = {} self.period = {} self.timer = {} self.smtp_max_count = db_retrieve_table_daemon( SMTP, entry='first').hourly_max self.email_count = 0 self.allowed_to_send_notice = True self.smtp_wait_timer = {} self.setup_conditionals()
def __init__(self, function, testing=False): super(CustomModule, self).__init__(function, testing=testing, name=__name__) self.timer_loop = time.time() self.control = DaemonControl() # Initialize custom options self.period = None self.start_offset = None self.select_measurement = None self.max_measure_age = None # Set custom options custom_function = db_retrieve_table_daemon(CustomController, unique_id=self.unique_id) self.setup_custom_options(FUNCTION_INFORMATION['custom_options'], custom_function) if not testing: self.initialize_variables()
def __init__(self, ready, unique_id): threading.Thread.__init__(self) super(TriggerController, self).__init__(ready, unique_id=unique_id, name=__name__) self.unique_id = unique_id self.sample_rate = None self.control = DaemonControl() self.pause_loop = False self.verify_pause_loop = True self.trigger = None self.trigger_type = None self.trigger_name = None self.is_activated = None self.log_level_debug = None self.smtp_max_count = None self.email_count = None self.allowed_to_send_notice = None self.smtp_wait_timer = None self.timer_period = None self.period = None self.smtp_wait_timer = None self.timer_start_time = None self.timer_end_time = None self.unique_id_1 = None self.unique_id_2 = None self.trigger_actions_at_period = None self.trigger_actions_at_start = None self.method_start_time = None self.method_end_time = None self.method_start_act = None # Infrared remote input self.lirc = None self.program = None self.word = None
def __init__(self, ready, unique_id, testing=False): threading.Thread.__init__(self) super(CustomModule, self).__init__(ready, unique_id=unique_id, name=__name__) self.unique_id = unique_id self.log_level_debug = None self.control_variable = None self.timestamp = None self.timer = None self.control = DaemonControl() self.outputIsOn = False self.timer_loop = time.time() # Initialize custom options self.measurement_device_id = None self.measurement_measurement_id = None self.output_device_id = None self.output_measurement_id = None self.output_channel_id = None self.setpoint = None self.hysteresis = None self.direction = None self.output_channel = None self.update_period = None # Set custom options custom_function = db_retrieve_table_daemon(CustomController, unique_id=unique_id) self.setup_custom_options(FUNCTION_INFORMATION['custom_options'], custom_function) self.output_channel = self.get_output_channel_from_channel_id( self.output_channel_id) self.initialize_variables()
def __init__(self, ready, unique_id, testing=False): threading.Thread.__init__(self) super(CustomModule, self).__init__(ready, unique_id=unique_id, name=__name__) self.unique_id = unique_id self.log_level_debug = None self.control = DaemonControl() # Initialize custom options self.text_1 = None self.integer_1 = None self.float_1 = None self.bool_1 = None self.select_1 = None self.select_measurement_1_device_id = None self.select_measurement_1_measurement_id = None self.output_1_device_id = None self.output_1_measurement_id = None self.output_1_channel_id = None self.select_device_1_id = None self.select_device_2_id = None # Set custom options custom_function = db_retrieve_table_daemon(CustomController, unique_id=unique_id) self.setup_custom_options(FUNCTION_INFORMATION['custom_options'], custom_function) # Get selected output channel number self.output_1_channel = self.get_output_channel_from_channel_id( self.output_1_channel_id) if not testing: pass
def pid_manipulate(pid_id, action): if action not in ['Hold', 'Pause', 'Resume']: flash('{}: {}'.format(TRANSLATIONS['invalid']['title'], action), "error") return 1 try: mod_pid = PID.query.filter(PID.unique_id == pid_id).first() if action == 'Hold': mod_pid.is_held = True mod_pid.is_paused = False elif action == 'Pause': mod_pid.is_paused = True mod_pid.is_held = False elif action == 'Resume': mod_pid.is_activated = True mod_pid.is_held = False mod_pid.is_paused = False db.session.commit() control = DaemonControl() return_value = None if action == 'Hold': return_value = control.pid_hold(pid_id) elif action == 'Pause': return_value = control.pid_pause(pid_id) elif action == 'Resume': return_value = control.pid_resume(pid_id) if return_value: flash( '{}: {}: {}: {}'.format(TRANSLATIONS['controller']['title'], TRANSLATIONS['pid']['title'], action, return_value), "success") except Exception as err: flash( "{}: {}: {}".format(TRANSLATIONS['Error']['title'], TRANSLATIONS['PID']['title'], err), "error")
def __init__(self, function, testing=False): super(CustomModule, self).__init__(function, testing=testing, name=__name__) self.autotune = None self.autotune_active = None self.control_variable = None self.timestamp = None self.timer_loop = None self.control = DaemonControl() # Initialize custom options self.measurement_device_id = None self.measurement_measurement_id = None self.output_device_id = None self.output_measurement_id = None self.output_channel_id = None self.setpoint = None self.period = None self.noiseband = None self.outstep = None self.direction = None self.output_channel = None # Set custom options custom_function = db_retrieve_table_daemon(CustomController, unique_id=self.unique_id) self.setup_custom_options(FUNCTION_INFORMATION['custom_options'], custom_function) self.output_channel = self.get_output_channel_from_channel_id( self.output_channel_id) if not testing: self.initialize_variables()
def pid_manipulate(pid_id, action): if action not in ['Hold', 'Pause', 'Resume']: flash(gettext("Invalid PID action: %(act)s", act=action), "error") return 1 try: mod_pid = PID.query.filter(PID.unique_id == pid_id).first() if action == 'Hold': mod_pid.is_held = True mod_pid.is_paused = False elif action == 'Pause': mod_pid.is_paused = True mod_pid.is_held = False elif action == 'Resume': mod_pid.is_activated = True mod_pid.is_held = False mod_pid.is_paused = False db.session.commit() control = DaemonControl() return_value = None if action == 'Hold': return_value = control.pid_hold(pid_id) elif action == 'Pause': return_value = control.pid_pause(pid_id) elif action == 'Resume': return_value = control.pid_resume(pid_id) if return_value: flash( gettext( "Daemon response to PID controller %(act)s command: " "%(rval)s", act=action, rval=return_value), "success") except Exception as err: flash(gettext("Error: %(err)s", err='PID: {msg}'.format(msg=err)), "error")
def pid_manipulate(pid_id, action): messages = { "success": [], "info": [], "warning": [], "error": [] } if action not in ['Hold', 'Pause', 'Resume']: messages["error"].append( '{}: {}'.format(TRANSLATIONS['invalid']['title'], action)) return messages try: control = DaemonControl() return_value = None if action == 'Hold': return_value = control.pid_hold(pid_id) elif action == 'Pause': return_value = control.pid_pause(pid_id) elif action == 'Resume': return_value = control.pid_resume(pid_id) if return_value: messages["success"].append( '{}: {}: {}: {}'.format( TRANSLATIONS['controller']['title'], TRANSLATIONS['pid']['title'], action, return_value)) except Exception as err: messages["error"].append( "{}: {}: {}".format( TRANSLATIONS['Error']['title'], TRANSLATIONS['PID']['title'], err)) return messages
def force_acquire_measurements(unique_id): action = '{action}, {controller}'.format( action=gettext("Force Measurements"), controller=TRANSLATIONS['input']['title']) error = [] try: mod_input = Input.query.filter( Input.unique_id == unique_id).first() if not mod_input.is_activated: error.append(gettext( "Activate controller before attempting to force the acquisition of measurements")) if not error: control = DaemonControl() status = control.input_force_measurements(unique_id) if status[0]: flash("Force Input Measurement: {}".format(status[1]), "error") else: flash("Force Input Measurement: {}".format(status[1]), "success") except Exception as except_msg: error.append(except_msg) flash_success_errors(error, action, url_for('routes_page.page_data'))
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.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.id) return return_str elif state == 'pause_pid': pid.is_paused = True pid.save() return_str = daemon.pid_pause(pid.id) return return_str elif state == 'hold_pid': pid.is_held = True pid.save() return_str = daemon.pid_hold(pid.id) return return_str elif state == 'resume_pid': pid.is_held = False pid.is_paused = False pid.save() return_str = daemon.pid_resume(pid.id) return return_str
def manipulate_output(action, output_id): """ Add, delete, and modify output settings while the daemon is active :param output_id: output ID in the SQL database :type output_id: str :param action: "Add", "Delete", or "Modify" :type action: str """ messages = { "success": [], "info": [], "warning": [], "error": [] } try: control = DaemonControl() return_values = control.output_setup(action, output_id) if return_values and len(return_values) > 1: if return_values[0]: messages["error"].append(gettext("%(err)s", err='{action} Output: Daemon response: {msg}'.format( action=action, msg=return_values[1]))) else: messages["success"].append(gettext("%(err)s", err='{action} Output: Daemon response: {msg}'.format( action=gettext(action), msg=return_values[1]))) except Exception as msg: messages["error"].append(gettext("%(err)s", err='{action} Output: Could not connect to Daemon: {error}'.format( action=action, error=msg))) return messages
def __init__(self, ready, unique_id): threading.Thread.__init__(self) super(ConditionalController, self).__init__(ready, unique_id=unique_id, name=__name__) self.unique_id = unique_id self.sample_rate = None self.control = DaemonControl() self.pause_loop = False self.verify_pause_loop = True self.is_activated = None self.smtp_max_count = None self.email_count = None self.allowed_to_send_notice = None self.smtp_wait_timer = None self.timer_period = None self.period = None self.start_offset = None self.refractory_period = None self.log_level_debug = None self.conditional_statement = None self.timer_refractory_period = None self.smtp_wait_timer = None self.timer_period = None self.timer_start_time = None self.timer_end_time = None self.unique_id_1 = None self.unique_id_2 = None self.trigger_actions_at_period = None self.trigger_actions_at_start = None self.method_start_time = None self.method_end_time = None self.method_start_act = None
def conditional_mod(form): """Modify a Conditional.""" messages = { "success": [], "info": [], "warning": [], "error": [], "page_refresh": True, "return_text": [], "name": None } try: if current_app.config['TESTING']: cmd_status = None pylint_message = "" else: messages[ "error"], lines_code, cmd_status, cmd_out = save_conditional_code( messages["error"], form.conditional_statement.data, form.conditional_status.data, form.function_id.data, ConditionalConditions.query.all(), Actions.query.all(), timeout=form.pyro_timeout.data, test=True) pylint_message = Markup( '<pre>\n\n' 'Full Conditional Statement code:\n\n{code}\n\n' 'Conditional Statement code analysis:\n\n{report}' '</pre>'.format(code=lines_code, report=cmd_out.decode("utf-8"))) cond_mod = Conditional.query.filter( Conditional.unique_id == form.function_id.data).first() cond_mod.name = form.name.data messages["name"] = form.name.data cond_mod.conditional_statement = form.conditional_statement.data cond_mod.conditional_status = form.conditional_status.data cond_mod.period = form.period.data cond_mod.log_level_debug = form.log_level_debug.data cond_mod.message_include_code = form.message_include_code.data cond_mod.start_offset = form.start_offset.data cond_mod.pyro_timeout = form.pyro_timeout.data if cmd_status: messages["warning"].append( "pylint returned with status: {}".format(cmd_status)) if pylint_message: messages["info"].append( "Review your code for issues and test before putting it " "into a production environment.") messages["return_text"].append(pylint_message) if not messages["error"]: db.session.commit() messages["success"].append('{action} {controller}'.format( action=TRANSLATIONS['modify']['title'], controller=TRANSLATIONS['conditional']['title'])) if cond_mod.is_activated: control = DaemonControl() return_value = control.refresh_daemon_conditional_settings( form.function_id.data) messages["success"].append( gettext("Daemon response: %(resp)s", resp=return_value)) except sqlalchemy.exc.OperationalError as except_msg: messages["error"].append(str(except_msg)) except sqlalchemy.exc.IntegrityError as except_msg: messages["error"].append(str(except_msg)) except Exception as except_msg: messages["error"].append(str(except_msg)) return messages
def pid_mod(form_mod_pid_base, form_mod_pid_pwm_raise, form_mod_pid_pwm_lower, form_mod_pid_relay_raise, form_mod_pid_relay_lower): action = u'{action} {controller}'.format( action=gettext(u"Modify"), controller=gettext(u"PID")) error = [] if not form_mod_pid_base.validate(): flash_form_errors(form_mod_pid_base) sensor_unique_id = form_mod_pid_base.measurement.data.split(',')[0] sensor = Input.query.filter( Input.unique_id == sensor_unique_id).first() if not sensor: error.append(gettext(u"A valid sensor is required")) mod_pid = PID.query.filter( PID.id == form_mod_pid_base.pid_id.data).first() # Check if a specific setting can be modified if the PID is active if mod_pid.is_activated: error = can_set_relay(error, form_mod_pid_base.pid_id.data, form_mod_pid_base.raise_relay_id.data, form_mod_pid_base.lower_relay_id.data) mod_pid.name = form_mod_pid_base.name.data mod_pid.measurement = form_mod_pid_base.measurement.data mod_pid.direction = form_mod_pid_base.direction.data mod_pid.period = form_mod_pid_base.period.data mod_pid.max_measure_age = form_mod_pid_base.max_measure_age.data mod_pid.setpoint = form_mod_pid_base.setpoint.data mod_pid.p = form_mod_pid_base.k_p.data mod_pid.i = form_mod_pid_base.k_i.data mod_pid.d = form_mod_pid_base.k_d.data mod_pid.integrator_min = form_mod_pid_base.integrator_max.data mod_pid.integrator_max = form_mod_pid_base.integrator_min.data if form_mod_pid_base.method_id.data: mod_pid.method_id = form_mod_pid_base.method_id.data else: mod_pid.method_id = None if form_mod_pid_base.raise_relay_id.data: raise_relay_type = Output.query.filter( Output.id == int(form_mod_pid_base.raise_relay_id.data)).first().relay_type if mod_pid.raise_relay_id == int(form_mod_pid_base.raise_relay_id.data): if raise_relay_type == 'pwm': if not form_mod_pid_pwm_raise.validate(): flash_form_errors(form_mod_pid_pwm_raise) else: mod_pid.raise_min_duration = form_mod_pid_pwm_raise.raise_min_duty_cycle.data mod_pid.raise_max_duration = form_mod_pid_pwm_raise.raise_max_duty_cycle.data else: if not form_mod_pid_relay_raise.validate(): flash_form_errors(form_mod_pid_relay_raise) else: mod_pid.raise_min_duration = form_mod_pid_relay_raise.raise_min_duration.data mod_pid.raise_max_duration = form_mod_pid_relay_raise.raise_max_duration.data mod_pid.raise_min_off_duration = form_mod_pid_relay_raise.raise_min_off_duration.data else: if raise_relay_type == 'pwm': mod_pid.raise_min_duration = 2 mod_pid.raise_max_duration = 98 else: mod_pid.raise_min_duration = 0 mod_pid.raise_max_duration = 0 mod_pid.raise_min_off_duration = 0 mod_pid.raise_relay_id = form_mod_pid_base.raise_relay_id.data else: mod_pid.raise_relay_id = None if form_mod_pid_base.lower_relay_id.data: lower_relay_type = Output.query.filter( Output.id == int(form_mod_pid_base.lower_relay_id.data)).first().relay_type if mod_pid.lower_relay_id == int(form_mod_pid_base.lower_relay_id.data): if lower_relay_type == 'pwm': if not form_mod_pid_pwm_lower.validate(): flash_form_errors(form_mod_pid_pwm_lower) else: mod_pid.lower_min_duration = form_mod_pid_pwm_lower.lower_min_duty_cycle.data mod_pid.lower_max_duration = form_mod_pid_pwm_lower.lower_max_duty_cycle.data else: if not form_mod_pid_relay_lower.validate(): flash_form_errors(form_mod_pid_relay_lower) else: mod_pid.lower_min_duration = form_mod_pid_relay_lower.lower_min_duration.data mod_pid.lower_max_duration = form_mod_pid_relay_lower.lower_max_duration.data mod_pid.lower_min_off_duration = form_mod_pid_relay_lower.lower_min_off_duration.data else: if lower_relay_type == 'pwm': mod_pid.lower_min_duration = 2 mod_pid.lower_max_duration = 98 else: mod_pid.lower_min_duration = 0 mod_pid.lower_max_duration = 0 mod_pid.lower_min_off_duration = 0 mod_pid.lower_relay_id = form_mod_pid_base.lower_relay_id.data else: mod_pid.lower_relay_id = None if (mod_pid.raise_relay_id and mod_pid.lower_relay_id and mod_pid.raise_relay_id == mod_pid.lower_relay_id): error.append(gettext(u"Raise and lower outputs cannot be the same")) try: if not error: db.session.commit() # If the controller is active or paused, refresh variables in thread if mod_pid.is_activated: control = DaemonControl() return_value = control.pid_mod(form_mod_pid_base.pid_id.data) flash(gettext( u"PID Controller settings refresh response: %(resp)s", resp=return_value), "success") except Exception as except_msg: error.append(except_msg) flash_success_errors(error, action, url_for('page_routes.page_pid'))
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_on(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 controller_activate_deactivate(controller_action, controller_type, controller_id): """ Activate or deactivate controller :param controller_action: Activate or deactivate :type controller_action: str :param controller_type: The controller type (LCD, Math, PID, Input, Timer) :type controller_type: str :param controller_id: Controller with ID to activate or deactivate :type controller_id: str """ if not user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) activated = bool(controller_action == 'activate') translated_names = { "Conditional": gettext("Conditional"), "Input": gettext("Input"), "LCD": gettext("LCD"), "Math": gettext("Math"), "PID": gettext("PID"), "Timer": gettext("Timer") } mod_controller = None if controller_type == 'Conditional': mod_controller = Conditional.query.filter( Conditional.id == int(controller_id)).first() elif controller_type == 'Input': mod_controller = Input.query.filter( Input.id == int(controller_id)).first() elif controller_type == 'LCD': mod_controller = LCD.query.filter(LCD.id == int(controller_id)).first() elif controller_type == 'Math': mod_controller = Math.query.filter( Math.id == int(controller_id)).first() elif controller_type == 'PID': mod_controller = PID.query.filter(PID.id == int(controller_id)).first() elif controller_type == 'Timer': mod_controller = Timer.query.filter( Timer.id == int(controller_id)).first() if mod_controller is None: flash( "{type} Controller {id} doesn't exist".format(type=controller_type, id=controller_id), "error") return redirect(url_for('routes_general.home')) try: mod_controller.is_activated = activated db.session.commit() if activated: flash( gettext("%(cont)s controller activated in SQL database", cont=translated_names[controller_type]), "success") else: flash( gettext("%(cont)s controller deactivated in SQL database", cont=translated_names[controller_type]), "success") except Exception as except_msg: flash( gettext("Error: %(err)s", err='SQL: {msg}'.format(msg=except_msg)), "error") try: control = DaemonControl() if controller_action == 'activate': return_values = control.controller_activate( controller_type, int(controller_id)) else: return_values = control.controller_deactivate( controller_type, int(controller_id)) if return_values[0]: flash("{err}".format(err=return_values[1]), "error") else: flash("{err}".format(err=return_values[1]), "success") except Exception as except_msg: flash( gettext("Error: %(err)s", err='Daemon: {msg}'.format(msg=except_msg)), "error")
def initialize_input(self): from mycodo.mycodo_client import DaemonControl self.control = DaemonControl() self.sensor = AM2315(self.input_dev.i2c_bus)
def __init__(self, ready, pid_id): threading.Thread.__init__(self) self.logger = logging.getLogger("mycodo.pid_{id}".format(id=pid_id)) self.running = False self.thread_startup_timer = timeit.default_timer() self.thread_shutdown_timer = 0 self.ready = ready self.pid_id = pid_id self.pid_unique_id = db_retrieve_table_daemon( PID, device_id=self.pid_id).unique_id self.control = DaemonControl() self.control_variable = 0.0 self.derivator = 0.0 self.integrator = 0.0 self.error = 0.0 self.P_value = None self.I_value = None self.D_value = None self.lower_seconds_on = 0.0 self.raise_seconds_on = 0.0 self.lower_duty_cycle = 0.0 self.raise_duty_cycle = 0.0 self.last_time = None self.last_measurement = None self.last_measurement_success = False self.is_activated = None self.is_held = None self.is_paused = None self.measurement = None self.method_id = None self.direction = None self.raise_output_id = None self.raise_min_duration = None self.raise_max_duration = None self.raise_min_off_duration = None self.lower_output_id = None self.lower_min_duration = None self.lower_max_duration = None self.lower_min_off_duration = None self.Kp = None self.Ki = None self.Kd = None self.integrator_min = None self.integrator_max = None self.period = None self.max_measure_age = None self.default_setpoint = None self.setpoint = None self.store_lower_as_negative = None # Hysteresis options self.band = None self.allow_raising = False self.allow_lowering = False self.dev_unique_id = None self.input_duration = None self.raise_output_type = None self.lower_output_type = None self.first_start = True self.initialize_values() self.timer = t.time() + self.period # Check if a method is set for this PID self.method_start_act = None if self.method_id: self.setup_method(self.method_id)
def last_data_pid(pid_id, input_period): """Return the most recent time and value from influxdb.""" if not current_user.is_authenticated: return "You are not logged in and cannot access this endpoint" if not str_is_float(input_period): return '', 204 try: pid = PID.query.filter(PID.unique_id == pid_id).first() if len(pid.measurement.split(',')) == 2: device_id = pid.measurement.split(',')[0] measurement_id = pid.measurement.split(',')[1] else: device_id = None measurement_id = None actual_measurement = DeviceMeasurements.query.filter( DeviceMeasurements.unique_id == measurement_id).first() if actual_measurement: actual_conversion = Conversion.query.filter( Conversion.unique_id == actual_measurement.conversion_id).first() else: actual_conversion = None (actual_channel, actual_unit, actual_measurement) = return_measurement_info(actual_measurement, actual_conversion) setpoint_unit = None if pid and ',' in pid.measurement: pid_measurement = pid.measurement.split(',')[1] setpoint_measurement = DeviceMeasurements.query.filter( DeviceMeasurements.unique_id == pid_measurement).first() if setpoint_measurement: conversion = Conversion.query.filter( Conversion.unique_id == setpoint_measurement.conversion_id).first() _, setpoint_unit, _ = return_measurement_info( setpoint_measurement, conversion) p_value = return_point_timestamp(pid_id, 'pid_value', input_period, measurement='pid_p_value') i_value = return_point_timestamp(pid_id, 'pid_value', input_period, measurement='pid_i_value') d_value = return_point_timestamp(pid_id, 'pid_value', input_period, measurement='pid_d_value') if None not in (p_value[1], i_value[1], d_value[1]): pid_value = [ p_value[0], f'{float(p_value[1]) + float(i_value[1]) + float(d_value[1]):.3f}' ] else: pid_value = None setpoint_band = None if pid.band: try: daemon = DaemonControl() setpoint_band = daemon.pid_get(pid.unique_id, 'setpoint_band') except: logger.debug("Couldn't get setpoint") live_data = { 'activated': pid.is_activated, 'paused': pid.is_paused, 'held': pid.is_held, 'setpoint': return_point_timestamp(pid_id, setpoint_unit, input_period, channel=0), 'setpoint_band': setpoint_band, 'pid_p_value': p_value, 'pid_i_value': i_value, 'pid_d_value': d_value, 'pid_pid_value': pid_value, 'duration_time': return_point_timestamp(pid_id, 's', input_period, measurement='duration_time'), 'duty_cycle': return_point_timestamp(pid_id, 'percent', input_period, measurement='duty_cycle'), 'actual': return_point_timestamp(device_id, actual_unit, input_period, measurement=actual_measurement, channel=actual_channel) } return jsonify(live_data) except KeyError: logger.debug("No Data returned form influxdb") return '', 204 except Exception as err: logger.exception(f"URL for 'last_pid' raised and error: {err}") return '', 204
def conditional_mod(form): """Modify a Conditional""" error = [] action = '{action} {controller}'.format( action=TRANSLATIONS['modify']['title'], controller=TRANSLATIONS['conditional']['title']) try: pre_statement = """import os, random, sys sys.path.append(os.path.abspath('/var/mycodo-root')) from mycodo.mycodo_client import DaemonControl control = DaemonControl() message = '' # The following functions are used to test the Conditional code. # The functions that are used in the production environment will # return the proper values and affect the proper systems when called. def measure(condition_id): # pylint: disable=unused-argument return random.choice([ None, -100000, -10000, -1000, -100, -10, 0, 1, 10, 100, 1000, 10000, 100000 ]) def measure_dict(condition_id): # pylint: disable=unused-argument return [ {'time': '2019-04-24T18:01:00.000Z', 'value': -100000}, {'time': '2019-04-24T18:02:00.000Z', 'value': -10000}, {'time': '2019-04-24T18:03:00.000Z', 'value': -1000}, {'time': '2019-04-24T18:04:00.000Z', 'value': -100}, {'time': '2019-04-24T18:05:00.000Z', 'value': -10}, {'time': '2019-04-24T18:06:00.000Z', 'value': -1}, {'time': '2019-04-24T18:07:00.000Z', 'value': 1}, {'time': '2019-04-24T18:08:00.000Z', 'value': 10}, {'time': '2019-04-24T18:09:00.000Z', 'value': 100}, {'time': '2019-04-24T18:10:00.000Z', 'value': 1000}, {'time': '2019-04-24T18:11:00.000Z', 'value': 10000}, {'time': '2019-04-24T18:12:00.000Z', 'value': 100000}, ] def run_all_actions(message=message): # pylint: disable=unused-argument pass def run_action(action_id, message=message): # pylint: disable=unused-argument pass ########################### ##### BEGIN USER CODE ##### ########################### """ cond_statement = (pre_statement + form.conditional_statement.data) if len(cond_statement.splitlines()) > 999: error.append( "Too many lines in code. Reduce code to less than 1000 lines.") lines_code = '' for line_num, each_line in enumerate(cond_statement.splitlines(), 1): if len(str(line_num)) == 3: line_spacing = '' elif len(str(line_num)) == 2: line_spacing = ' ' else: line_spacing = ' ' lines_code += '{sp}{ln}: {line}\n'.format(sp=line_spacing, ln=line_num, line=each_line) path_file = '/tmp/conditional_code_{}.py'.format( str(uuid.uuid4()).split('-')[0]) with open(path_file, 'w') as out: out.write('{}\n'.format(cond_statement)) cmd_test = 'export PYTHONPATH=$PYTHONPATH:/var/mycodo-root && ' \ 'pylint3 -d I,W0621,C0103,C0111,C0301,C0327,C0410,C0413 {path}'.format( path=path_file) cmd_out, cmd_err, cmd_status = cmd_output(cmd_test) os.remove(path_file) message = Markup('<pre>\n\n' 'Full Conditional Statement code:\n\n{code}\n\n' 'Conditional Statement code analysis:\n\n{report}' '</pre>'.format(code=lines_code, report=cmd_out.decode("utf-8"))) if cmd_status: flash( 'Error(s) were found while evaluating your code. Review ' 'the error(s), below, and fix them before activating your ' 'Conditional.', 'error') flash(message, 'error') else: flash( "No errors were found while evaluating your code. However, " "this doesn't mean your code will perform as expected. " "Review your code for issues and test your Conditional " "before putting it into a production environment.", 'success') flash(message, 'success') cond_mod = Conditional.query.filter( Conditional.unique_id == form.function_id.data).first() cond_mod.name = form.name.data cond_mod.conditional_statement = form.conditional_statement.data cond_mod.period = form.period.data cond_mod.log_level_debug = form.log_level_debug.data cond_mod.start_offset = form.start_offset.data cond_mod.refractory_period = form.refractory_period.data if not error: db.session.commit() if cond_mod.is_activated: control = DaemonControl() return_value = control.refresh_daemon_conditional_settings( form.function_id.data) flash(gettext("Daemon response: %(resp)s", resp=return_value), "success") except sqlalchemy.exc.OperationalError as except_msg: error.append(except_msg) except sqlalchemy.exc.IntegrityError as except_msg: error.append(except_msg) except Exception as except_msg: error.append(except_msg) flash_success_errors(error, action, url_for('routes_page.page_function'))
def controller_activate_deactivate(controller_action, controller_type, controller_id): """ Activate or deactivate controller :param controller_action: Activate or deactivate :type controller_action: str :param controller_type: The controller type (Conditional, LCD, Math, PID, Input) :type controller_type: str :param controller_id: Controller with ID to activate or deactivate :type controller_id: str """ if not user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) error = [] activated = bool(controller_action == 'activate') translated_names = { "Conditional": TRANSLATIONS['conditional']['title'], "Input": TRANSLATIONS['input']['title'], "LCD": TRANSLATIONS['lcd']['title'], "Math": TRANSLATIONS['math']['title'], "PID": TRANSLATIONS['pid']['title'], "Trigger": TRANSLATIONS['trigger']['title'] } mod_controller = None if controller_type == 'Conditional': mod_controller = Conditional.query.filter( Conditional.unique_id == controller_id).first() elif controller_type == 'Input': mod_controller = Input.query.filter( Input.unique_id == controller_id).first() if activated: error = check_for_valid_unit_and_conversion(controller_id, error) elif controller_type == 'LCD': mod_controller = LCD.query.filter( LCD.unique_id == controller_id).first() elif controller_type == 'Math': mod_controller = Math.query.filter( Math.unique_id == controller_id).first() if activated: error = check_for_valid_unit_and_conversion(controller_id, error) elif controller_type == 'PID': mod_controller = PID.query.filter( PID.unique_id == controller_id).first() if activated: error = check_for_valid_unit_and_conversion(controller_id, error) elif controller_type == 'Trigger': mod_controller = Trigger.query.filter( Trigger.unique_id == controller_id).first() if mod_controller is None: flash("{type} Controller {id} doesn't exist".format( type=controller_type, id=controller_id), "error") return redirect(url_for('routes_general.home')) try: if not error: mod_controller.is_activated = activated db.session.commit() if activated: flash( "{} {} (SQL)".format( translated_names[controller_type], TRANSLATIONS['activate']['title']), "success") else: flash( "{} {} (SQL)".format( translated_names[controller_type], TRANSLATIONS['deactivate']['title']), "success") except Exception as except_msg: flash(gettext("Error: %(err)s", err='SQL: {msg}'.format(msg=except_msg)), "error") try: if not error: control = DaemonControl() if controller_action == 'activate': return_values = control.controller_activate( controller_type,controller_id) else: return_values = control.controller_deactivate( controller_type, controller_id) if return_values[0]: flash("{err}".format(err=return_values[1]), "error") else: flash("{err}".format(err=return_values[1]), "success") except Exception as except_msg: flash('{}: {}'.format(TRANSLATIONS['error']['title'], except_msg), "error") for each_error in error: flash(each_error, 'error')
def widget_add(form_base, request_form): """Add a widget to the dashboard""" action = '{action} {controller}'.format( action=TRANSLATIONS['add']['title'], controller=TRANSLATIONS['widget']['title']) error = [] dict_widgets = parse_widget_information() if form_base.widget_type.data: widget_name = form_base.widget_type.data else: widget_name = '' error.append("Missing widget name") if current_app.config['TESTING']: dep_unmet = False else: dep_unmet, _ = return_dependencies(widget_name) if dep_unmet: list_unmet_deps = [] for each_dep in dep_unmet: list_unmet_deps.append(each_dep[0]) error.append("The {dev} device you're trying to add has unmet dependencies: {dep}".format( dev=widget_name, dep=', '.join(list_unmet_deps))) new_widget = Widget() new_widget.dashboard_id = form_base.dashboard_id.data new_widget.graph_type = widget_name new_widget.name = form_base.name.data new_widget.font_em_name = form_base.font_em_name.data new_widget.enable_drag_handle = form_base.enable_drag_handle.data new_widget.refresh_duration = form_base.refresh_duration.data # Find where the next widget should be placed on the grid # Finds the lowest position to create as the new Widget's starting position position_y_start = 0 for each_widget in Widget.query.filter( Widget.dashboard_id == form_base.dashboard_id.data).all(): highest_position = each_widget.position_y + each_widget.height if highest_position > position_y_start: position_y_start = highest_position new_widget.position_y = position_y_start # widget add options if widget_name in dict_widgets: def dict_has_value(key): if (key in dict_widgets[widget_name] and (dict_widgets[widget_name][key] or dict_widgets[widget_name][key] == 0)): return True if dict_has_value('widget_width'): new_widget.width = dict_widgets[widget_name]['widget_width'] if dict_has_value('widget_height'): new_widget.height = dict_widgets[widget_name]['widget_height'] # Generate string to save from custom options error, custom_options = custom_options_return_json( error, dict_widgets, request_form, device=widget_name, use_defaults=True) new_widget.custom_options = custom_options # # Execute at Creation # if ('execute_at_creation' in dict_widgets[widget_name] and not current_app.config['TESTING']): dict_widgets[widget_name]['execute_at_creation']( new_widget, dict_widgets[widget_name]) try: if not error: new_widget.save() # Refresh widget settings control = DaemonControl() control.widget_add_refresh(new_widget.unique_id) flash(gettext( "{dev} with ID %(id)s successfully added".format( dev=dict_widgets[form_base.widget_type.data]['widget_name']), id=new_widget.id), "success") except sqlalchemy.exc.OperationalError as except_msg: error.append(except_msg) except sqlalchemy.exc.IntegrityError as except_msg: error.append(except_msg) return dep_unmet