def setup_method(self, method_id):
        """ Initialize method variables to start running a method """
        self.setpoint_tracking_id = ''

        method = load_method_handler(method_id, self.logger)

        this_controller = db_retrieve_table_daemon(PID,
                                                   unique_id=self.unique_id)
        self.method_type = method.method_type

        if parse_db_time(this_controller.method_start_time) is None:
            self.method_start_time = datetime.datetime.now()
            self.method_end_time = method.determine_end_time(
                self.method_start_time)

            self.logger.info("Starting method {} {}".format(
                self.method_start_time, self.method_end_time))

            with session_scope(MYCODO_DB_PATH) as db_session:
                this_controller = db_session.query(PID)
                this_controller = this_controller.filter(
                    PID.unique_id == self.unique_id).first()
                this_controller.method_start_time = self.method_start_time
                this_controller.method_end_time = self.method_end_time
                db_session.commit()
        else:
            # already running, potentially the daemon has been restarted
            self.method_start_time = this_controller.method_start_time
            self.method_end_time = this_controller.method_end_time

        self.setpoint_tracking_id = method_id
        self.logger.debug(
            "Method enabled: {id}".format(id=self.setpoint_tracking_id))
    def start_method(self, method_id):
        """Instruct a method to start running."""
        if method_id:
            this_controller = db_retrieve_table_daemon(
                Trigger, unique_id=self.unique_id)

            method = load_method_handler(method_id, self.logger)

            if parse_db_time(this_controller.method_start_time) is None:
                self.method_start_time = datetime.datetime.now()
                self.method_end_time = method.determine_end_time(
                    self.method_start_time)

                self.logger.info(
                    f"Starting method {self.method_start_time} {self.method_end_time}"
                )

                with session_scope(MYCODO_DB_PATH) as db_session:
                    this_controller = db_session.query(Trigger)
                    this_controller = this_controller.filter(
                        Trigger.unique_id == self.unique_id).first()
                    this_controller.method_start_time = self.method_start_time
                    this_controller.method_end_time = self.method_end_time
                    db_session.commit()
            else:
                # already running, potentially the daemon has been restarted
                self.method_start_time = this_controller.method_start_time
                self.method_end_time = this_controller.method_end_time
    def get_method_output(self, method_id):
        """ Get output variable from method """
        this_controller = db_retrieve_table_daemon(
            Trigger, unique_id=self.unique_id)

        if this_controller.method_start_time is None:
            return

        now = datetime.datetime.now()

        method = load_method_handler(method_id, self.logger)
        setpoint, ended = method.calculate_setpoint(now, this_controller.method_start_time)

        if setpoint is not None:
            if setpoint > 100:
                setpoint = 100
            elif setpoint < 0:
                setpoint = 0

        return setpoint, ended
    def check_pid(self):
        """ Get measurement and apply to PID controller """
        # If PID is active, retrieve measurement and update
        # the control variable.
        # A PID on hold will sustain the current output and
        # not update the control variable.
        if self.is_activated and (not self.is_paused or not self.is_held):
            self.get_last_measurement_pid()

            if self.last_measurement_success:
                if self.setpoint_tracking_type == 'method' and self.setpoint_tracking_id != '':
                    # Update setpoint using a method
                    this_pid = db_retrieve_table_daemon(
                        PID, unique_id=self.unique_id)

                    now = datetime.datetime.now()

                    method = load_method_handler(self.setpoint_tracking_id,
                                                 self.logger)
                    new_setpoint, ended = method.calculate_setpoint(
                        now, this_pid.method_start_time)
                    self.logger.debug("Method {} {} {} {}".format(
                        self.setpoint_tracking_id, method, now,
                        this_pid.method_start_time))

                    if ended:
                        # point in time is out of method range
                        with session_scope(MYCODO_DB_PATH) as db_session:
                            # Overwrite this_controller with committable connection
                            this_pid = db_session.query(PID).filter(
                                PID.unique_id == self.unique_id).first()

                            self.logger.debug("Ended")
                            # Duration method has ended, reset method_start_time locally and in DB
                            this_pid.method_start_time = None
                            this_pid.method_end_time = None
                            this_pid.is_activated = False
                            db_session.commit()

                            self.is_activated = False
                            self.stop_controller()

                            db_session.commit()

                    if new_setpoint is not None:
                        self.logger.debug("New setpoint = {} {}".format(
                            new_setpoint, ended))
                        self.PID_Controller.setpoint = new_setpoint
                    else:
                        self.logger.debug(
                            "New setpoint = default {} {}".format(
                                self.setpoint, ended))
                        self.PID_Controller.setpoint = self.setpoint

                if self.setpoint_tracking_type == 'input-math' and self.setpoint_tracking_id != '':
                    # Update setpoint using an Input or Math
                    device_id = self.setpoint_tracking_id.split(',')[0]
                    measurement_id = self.setpoint_tracking_id.split(',')[1]

                    measurement = get_measurement(measurement_id)
                    if not measurement:
                        return False, None

                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        measurement, conversion)

                    last_measurement = read_last_influxdb(
                        device_id,
                        unit,
                        channel,
                        measure=measurement,
                        duration_sec=self.setpoint_tracking_max_age)

                    if last_measurement[1] is not None:
                        self.PID_Controller.setpoint = last_measurement[1]
                    else:
                        self.logger.debug(
                            "Could not find measurement for Setpoint "
                            "Tracking. Max Age of {} exceeded for measuring "
                            "device ID {} (measurement {})".format(
                                self.setpoint_tracking_max_age, device_id,
                                measurement_id))
                        self.PID_Controller.setpoint = None

                # Calculate new control variable (output) from PID Controller
                self.PID_Controller.update_pid_output(self.last_measurement)

                self.write_pid_values()  # Write variables to database

        # Is PID in a state that allows manipulation of outputs
        if (self.is_activated and self.PID_Controller.setpoint is not None
                and (not self.is_paused or self.is_held)):
            self.manipulate_output()