Esempio n. 1
0
    def get_measurements_from_str(self, device):
        try:
            measurements = []
            device_list = device.split(';')
            for each_device_set in device_list:
                device_id = each_device_set.split(',')[0]
                device_measure_id = each_device_set.split(',')[1]

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

                last_measurement = read_last_influxdb(
                    device_id,
                    measurement.unit,
                    measurement.measurement,
                    measurement.channel,
                    self.max_measure_age)
                if not last_measurement:
                    return False, None
                else:
                    measurements.append(last_measurement[1])
            return True, measurements
        except urllib3.exceptions.NewConnectionError:
            return False, "Influxdb: urllib3.exceptions.NewConnectionError"
        except Exception as msg:
            return False, "Influxdb: Unknown Error: {err}".format(err=msg)
Esempio n. 2
0
    def get_last_measurement(self):
        """
        Retrieve the latest input measurement from InfluxDB

        :rtype: None
        """
        self.last_measurement_success = False

        # Get latest measurement from influxdb
        try:
            device_measurement = get_measurement(self.measurement_id)

            if device_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=device_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)

            self.last_measurement = read_last_influxdb(
                self.device_id,
                unit,
                measurement,
                channel,
                int(self.max_measure_age))

            if self.last_measurement:
                self.last_time = self.last_measurement[0]
                self.last_measurement = self.last_measurement[1]

                utc_dt = datetime.datetime.strptime(
                    self.last_time.split(".")[0],
                    '%Y-%m-%dT%H:%M:%S')
                utc_timestamp = calendar.timegm(utc_dt.timetuple())
                local_timestamp = str(datetime.datetime.fromtimestamp(utc_timestamp))
                self.logger.debug("Latest (CH{ch}, Unit: {unit}): {last} @ {ts}".format(
                    ch=channel,
                    unit=unit,
                    last=self.last_measurement,
                    ts=local_timestamp))
                if calendar.timegm(time.gmtime()) - utc_timestamp > self.max_measure_age:
                    self.logger.error(
                        "Last measurement was {last_sec} seconds ago, however"
                        " the maximum measurement age is set to {max_sec}"
                        " seconds.".format(
                            last_sec=calendar.timegm(time.gmtime()) - utc_timestamp,
                            max_sec=self.max_measure_age
                        ))
                self.last_measurement_success = True
            else:
                self.logger.warning("No data returned from influxdb")
        except requests.ConnectionError:
            self.logger.error("Failed to read measurement from the "
                              "influxdb database: Could not connect.")
        except Exception as except_msg:
            self.logger.exception(
                "Exception while reading measurement from the influxdb "
                "database: {err}".format(err=except_msg))
Esempio n. 3
0
    def get_measurements_from_id(self, device_id, measure_id):
        measurement = get_measurement(measure_id)

        measurement = read_last_influxdb(
            device_id,
            measurement.unit,
            measurement.measurement,
            measurement.channel,
            self.max_measure_age)
        if not measurement:
            return False, None
        return True, measurement
    def loop(self):
        if self.timer_loop < time.time():
            while self.timer_loop < time.time():
                self.timer_loop += self.period

            measure = []
            for each_id_set in self.select_measurement:
                device_device_id = each_id_set.split(",")[0]
                device_measure_id = each_id_set.split(",")[1]

                device_measurement = get_measurement(device_measure_id)

                if not device_measurement:
                    self.logger.error("Could not find Device Measurement")
                    return

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

                last_measurement = read_last_influxdb(
                    device_device_id,
                    unit,
                    channel,
                    measure=measurement,
                    duration_sec=self.max_measure_age)

                if not last_measurement:
                    self.logger.error(
                        "Could not find measurement within the set Max Age for Device {} and Measurement {}"
                        .format(device_device_id, device_measure_id))
                    if self.halt_on_missing_measure:
                        self.logger.debug(
                            "Instructed to halt on the first missing measurement. Halting."
                        )
                        return False
                else:
                    measure.append(last_measurement[1])

            if len(measure) > 1:
                stat_mean = float(sum(measure) / float(len(measure)))
                stat_median = median(measure)
                stat_minimum = min(measure)
                stat_maximum = max(measure)
                stdev_ = stdev(measure)
                stdev_mean_upper = stat_mean + stdev_
                stdev_mean_lower = stat_mean - stdev_

                list_measurement = [
                    stat_mean, stat_median, stat_minimum, stat_maximum, stdev_,
                    stdev_mean_upper, stdev_mean_lower
                ]

                for each_channel, each_measurement in self.channels_measurement.items(
                ):
                    if each_measurement.is_enabled:
                        channel, unit, measurement = return_measurement_info(
                            each_measurement,
                            self.channels_conversion[each_channel])

                        self.logger.debug(
                            "Saving {} to channel {} with measurement {} and unit {}"
                            .format(list_measurement[each_channel],
                                    each_channel, measurement, unit))

                        write_influxdb_value(
                            self.unique_id,
                            unit,
                            value=list_measurement[each_channel],
                            measure=measurement,
                            channel=each_channel)
            else:
                self.logger.debug(
                    "Less than 2 measurements found within Max Age. "
                    "Calculations need at least 2 measurements. Not calculating."
                )
Esempio n. 5
0
def math_mod(form_mod_math, form_mod_type=None):
    action = '{action} {controller}'.format(
        action=TRANSLATIONS['modify']['title'],
        controller=TRANSLATIONS['math']['title'])
    error = []

    try:
        mod_math = Math.query.filter(
            Math.unique_id == form_mod_math.math_id.data).first()

        if mod_math.is_activated:
            error.append(
                gettext("Deactivate Math controller before modifying its "
                        "settings"))

        if mod_math.math_type != 'redundancy' and form_mod_type and not form_mod_type.validate(
        ):
            error.append(gettext("Error in form field(s)"))
            flash_form_errors(form_mod_type)

        mod_math.name = form_mod_math.name.data
        mod_math.period = form_mod_math.period.data
        mod_math.max_measure_age = form_mod_math.max_measure_age.data
        mod_math.start_offset = form_mod_math.start_offset.data

        measurements = DeviceMeasurements.query.filter(
            DeviceMeasurements.device_id == form_mod_math.math_id.data).all()

        # Set each measurement to the same measurement/unit
        if form_mod_math.select_measurement_unit.data:
            for each_measurement in measurements:
                if ',' in form_mod_math.select_measurement_unit.data:
                    each_measurement.measurement = form_mod_math.select_measurement_unit.data.split(
                        ',')[0]
                    each_measurement.unit = form_mod_math.select_measurement_unit.data.split(
                        ',')[1]
                else:
                    each_measurement.measurement = ''
                    each_measurement.unit = ''

        # Enable/disable Channels
        if form_mod_math.measurements_enabled.data:
            for each_measurement in measurements:
                if each_measurement.unique_id in form_mod_math.measurements_enabled.data:
                    each_measurement.is_enabled = True
                else:
                    each_measurement.is_enabled = False

        original_inputs = mod_math.inputs

        # Collect inputs and measurement name and units
        if mod_math.math_type in [
                'average', 'difference', 'redundancy', 'statistics', 'sum',
                'verification'
        ]:
            if len(form_mod_math.inputs.data) < 2:
                error.append("At least two Inputs must be selected")
            if form_mod_math.inputs.data:
                inputs_joined = ";".join(form_mod_math.inputs.data)
                mod_math.inputs = inputs_joined
            else:
                mod_math.inputs = ''

        if mod_math.math_type == 'average_single':
            mod_math.inputs = form_mod_type.average_input.data

            # Change measurement information
            if form_mod_type.average_input.data and ',' in form_mod_type.average_input.data:
                measurement_id = form_mod_type.average_input.data.split(',')[1]

                if measurement_id == 'output':
                    output = Output.query.filter(
                        Output.unique_id == form_mod_type.sum_input.data.split(
                            ',')[0]).first()
                    unit = output.unit
                    measurement = output.measurement
                else:
                    selected_measurement = get_measurement(measurement_id)
                    if selected_measurement:
                        conversion = Conversion.query.filter(
                            Conversion.unique_id ==
                            selected_measurement.conversion_id).first()
                    else:
                        conversion = None
                    _, unit, measurement = return_measurement_info(
                        selected_measurement, conversion)

                mod_measurement = DeviceMeasurements.query.filter(
                    DeviceMeasurements.device_id ==
                    form_mod_math.math_id.data).first()
                mod_measurement.measurement = measurement
                mod_measurement.unit = unit

        if mod_math.math_type == 'sum_single':
            mod_math.inputs = form_mod_type.sum_input.data

            # Change measurement information
            if form_mod_type.sum_input.data and ',' in form_mod_type.sum_input.data:
                measurement_id = form_mod_type.sum_input.data.split(',')[1]

                if measurement_id == 'output':
                    output = Output.query.filter(
                        Output.unique_id == form_mod_type.sum_input.data.split(
                            ',')[0]).first()
                    unit = output.unit
                    measurement = output.measurement
                else:
                    selected_measurement = get_measurement(measurement_id)
                    if selected_measurement:
                        conversion = Conversion.query.filter(
                            Conversion.unique_id ==
                            selected_measurement.conversion_id).first()
                    else:
                        conversion = None
                    _, unit, measurement = return_measurement_info(
                        selected_measurement, conversion)

                mod_measurement = DeviceMeasurements.query.filter(
                    DeviceMeasurements.device_id ==
                    form_mod_math.math_id.data).first()
                mod_measurement.measurement = measurement
                mod_measurement.unit = unit

        elif mod_math.math_type == 'redundancy':
            # If input selection changes, create the default order list that can then be modified
            if original_inputs != ';'.join(form_mod_math.inputs.data):
                mod_math.order_of_use = ';'.join(form_mod_math.inputs.data)
            else:
                # Ensure order_of_use includes all input IDs and is properly formatted
                if mod_math.inputs:
                    mod_math.order_of_use = ';'.join(
                        form_mod_type.order_of_use.data)

        elif mod_math.math_type == 'difference':
            if len(form_mod_math.inputs.data) != 2:
                error.append("Only two Inputs must be selected")
            mod_math.difference_reverse_order = form_mod_type.difference_reverse_order.data
            mod_math.difference_absolute = form_mod_type.difference_absolute.data

        elif mod_math.math_type == 'equation':
            mod_math.equation_input = form_mod_type.equation_input.data
            mod_math.equation = form_mod_type.equation.data

        elif mod_math.math_type == 'humidity':
            mod_math.dry_bulb_t_id = form_mod_type.dry_bulb_temperature.data.split(
                ',')[0]
            mod_math.dry_bulb_t_measure_id = form_mod_type.dry_bulb_temperature.data.split(
                ',')[1]
            dbt_input = Input.query.filter(
                Input.unique_id == mod_math.dry_bulb_t_id).first()
            dbt_math = Input.query.filter(
                Math.unique_id == mod_math.dry_bulb_t_id).first()
            if not dbt_input and not dbt_math:
                error.append(
                    "Invalid dry-bulb temperature selection: Must be a valid Input or Math"
                )

            mod_math.wet_bulb_t_id = form_mod_type.wet_bulb_temperature.data.split(
                ',')[0]
            mod_math.wet_bulb_t_measure_id = form_mod_type.wet_bulb_temperature.data.split(
                ',')[1]
            wbt_input = Input.query.filter(
                Input.unique_id == mod_math.wet_bulb_t_id).first()
            wbt_math = Input.query.filter(
                Math.unique_id == mod_math.wet_bulb_t_id).first()
            if not wbt_input and not wbt_math:
                error.append(
                    "Invalid wet-bulb temperature selection: Must be a valid Input or Math"
                )

            if form_mod_type.pressure.data:
                mod_math.pressure_pa_id = form_mod_type.pressure.data.split(
                    ',')[0]
                mod_math.pressure_pa_measure_id = form_mod_type.pressure.data.split(
                    ',')[1]
            else:
                mod_math.pressure_pa_id = None
                mod_math.pressure_pa_measure_id = None

        elif mod_math.math_type == 'verification':
            mod_math.max_difference = form_mod_type.max_difference.data

        elif mod_math.math_type == 'vapor_pressure_deficit':
            mod_math.unique_id_1 = form_mod_type.unique_id_1.data.split(',')[0]
            mod_math.unique_measurement_id_1 = form_mod_type.unique_id_1.data.split(
                ',')[1]
            vpd_input = Input.query.filter(
                Input.unique_id == mod_math.unique_id_1).first()
            vpd_math = Input.query.filter(
                Math.unique_id == mod_math.unique_id_1).first()
            if not vpd_input and not vpd_math:
                error.append(
                    "Invalid vapor pressure deficit temperature selection: Must be a valid Input or Math"
                )

            mod_math.unique_id_2 = form_mod_type.unique_id_2.data.split(',')[0]
            mod_math.unique_measurement_id_2 = form_mod_type.unique_id_2.data.split(
                ',')[1]
            vpd_input = Input.query.filter(
                Input.unique_id == mod_math.unique_id_2).first()
            vpd_math = Input.query.filter(
                Math.unique_id == mod_math.unique_id_2).first()
            if not vpd_input and not vpd_math:
                error.append(
                    "Invalid vapor pressure deficit humidity selection: Must be a valid Input or Math"
                )

        if not error:
            db.session.commit()
    except Exception as except_msg:
        logger.exception(1)
        error.append(except_msg)

    flash_success_errors(error, action, url_for('routes_page.page_data'))
Esempio n. 6
0
    def get_last_measurement_pid(self):
        """
        Retrieve the latest input measurement from InfluxDB

        :rtype: None
        """
        self.last_measurement_success = False

        # Get latest measurement from influxdb
        try:
            device_measurement = get_measurement(self.measurement_id)

            if device_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=device_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)

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

            if self.last_measurement:
                self.last_time = self.last_measurement[0]
                self.last_measurement = self.last_measurement[1]

                utc_dt = datetime.datetime.strptime(
                    self.last_time.split(".")[0], '%Y-%m-%dT%H:%M:%S')
                utc_timestamp = calendar.timegm(utc_dt.timetuple())
                local_timestamp = str(
                    datetime.datetime.fromtimestamp(utc_timestamp))
                self.logger.debug(
                    "Latest (CH{ch}, Unit: {unit}): {last} @ {ts}".format(
                        ch=channel,
                        unit=unit,
                        last=self.last_measurement,
                        ts=local_timestamp))
                if calendar.timegm(
                        time.gmtime()) - utc_timestamp > self.max_measure_age:
                    self.logger.error(
                        "Last measurement was {last_sec} seconds ago, however"
                        " the maximum measurement age is set to {max_sec}"
                        " seconds.".format(
                            last_sec=calendar.timegm(time.gmtime()) -
                            utc_timestamp,
                            max_sec=self.max_measure_age))
                self.last_measurement_success = True
            else:
                self.logger.warning("No data returned from influxdb")
        except requests.ConnectionError:
            self.logger.error(
                "Failed to read measurement from the influxdb database: Could not connect."
            )
        except Exception as except_msg:
            self.logger.exception(
                "Exception while reading measurement from the influxdb database: {err}"
                .format(err=except_msg))
Esempio n. 7
0
    def get_measurement(self):
        """Gets the sensor's Electrical Conductivity measurement"""
        if not self.atlas_device.setup:
            self.logger.error("Error 101: Device not set up. See https://kizniche.github.io/Mycodo/Error-Codes#error-101 for more info.")
            return

        return_string = None
        self.return_dict = copy.deepcopy(measurements_dict)

        # Compensate measurement based on a temperature measurement
        if self.temperature_comp_meas_measurement_id and self.atlas_command:
            self.logger.debug("pH sensor set to calibrate temperature")

            last_measurement = self.get_last_measurement(
                self.temperature_comp_meas_device_id,
                self.temperature_comp_meas_measurement_id,
                max_age=self.max_age)

            if last_measurement and len(last_measurement) > 1:
                device_measurement = get_measurement(
                    self.temperature_comp_meas_measurement_id)
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=device_measurement.conversion_id)
                _, unit, _ = return_measurement_info(
                    device_measurement, conversion)

                if unit != "C":
                    out_value = convert_from_x_to_y_unit(
                        unit, "C", last_measurement[1])
                else:
                    out_value = last_measurement[1]

                self.logger.debug(
                    "Latest temperature used to calibrate: {temp}".format(
                        temp=out_value))

                ret_value, ret_msg = self.atlas_command.calibrate(
                    'temperature', set_amount=out_value)
                time.sleep(0.5)

                self.logger.debug("Calibration returned: {val}, {msg}".format(
                    val=ret_value, msg=ret_msg))
            else:
                self.logger.error(
                    "Calibration measurement not found within the past "
                    "{} seconds".format(self.max_age))

        # Read device
        atlas_status, atlas_return = self.atlas_device.query('R')
        self.logger.debug("Device Returned: {}: {}".format(atlas_status, atlas_return))

        if atlas_status == 'error':
            self.logger.error("Sensor read unsuccessful: {err}".format(err=atlas_return))
            return

        # Parse device return data
        if self.interface in ['FTDI', 'UART']:
            # Check for "check probe"
            for each_split in atlas_return:
                if 'check probe' in each_split:
                    self.logger.error('"check probe" returned from sensor')
                    return

            # Find float value in list
            for each_split in atlas_return:
                if "," in each_split or str_is_float(each_split):
                    return_string = each_split
                    break

        elif self.interface == 'I2C':
            return_string = atlas_return

        if return_string and ',' in return_string:
            # Multiple values returned
            index_place = 0
            return_list = return_string.split(',')
            if (self.is_enabled(0) and
                    len(return_list) > index_place and
                    str_is_float(return_list[index_place])):
                self.value_set(0, float(return_list[index_place]))
                index_place += 1
            if (self.is_enabled(1) and
                    len(return_list) > index_place and
                    str_is_float(return_list[index_place])):
                self.value_set(1, float(return_list[index_place]))
                index_place += 1
            if (self.is_enabled(2) and
                    len(return_list) > index_place and
                    str_is_float(return_list[index_place])):
                self.value_set(2, float(return_list[index_place]))
                index_place += 1
            if (self.is_enabled(3) and
                    len(return_list) > index_place and
                    str_is_float(return_list[index_place])):
                self.value_set(3, float(return_list[index_place]))
        elif str_is_float(return_string):
            # Single value returned
            if self.is_enabled(0):
                self.value_set(0, float(return_string))
            elif self.is_enabled(1):
                self.value_set(1, float(return_string))
            elif self.is_enabled(2):
                self.value_set(2, float(return_string))
            elif self.is_enabled(3):
                self.value_set(3, float(return_string))

        return self.return_dict
Esempio n. 8
0
    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)
                    new_setpoint, ended = calculate_method_setpoint(
                        self.setpoint_tracking_id, PID, this_pid, Method,
                        MethodData, self.logger)
                    if ended:
                        self.method_start_act = 'Ended'
                    if new_setpoint is not None:
                        self.PID_Controller.setpoint = new_setpoint
                    else:
                        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()
Esempio n. 9
0
def math_mod(form_mod_math, form_mod_type=None):
    action = '{action} {controller}'.format(
        action=TRANSLATIONS['modify']['title'],
        controller=TRANSLATIONS['math']['title'])
    error = []

    try:
        mod_math = Math.query.filter(
            Math.unique_id == form_mod_math.math_id.data).first()

        if mod_math.is_activated:
            error.append(gettext(
                "Deactivate Math controller before modifying its "
                "settings"))

        if mod_math.math_type != 'redundancy' and form_mod_type and not form_mod_type.validate():
            error.append(gettext("Error in form field(s)"))
            flash_form_errors(form_mod_type)

        mod_math.name = form_mod_math.name.data
        mod_math.period = form_mod_math.period.data
        mod_math.max_measure_age = form_mod_math.max_measure_age.data
        mod_math.start_offset = form_mod_math.start_offset.data

        measurements = DeviceMeasurements.query.filter(
            DeviceMeasurements.device_id == form_mod_math.math_id.data).all()

        # Set each measurement to the same measurement/unit
        if form_mod_math.select_measurement_unit.data:
            for each_measurement in measurements:
                if ',' in form_mod_math.select_measurement_unit.data:
                    each_measurement.measurement = form_mod_math.select_measurement_unit.data.split(',')[0]
                    each_measurement.unit = form_mod_math.select_measurement_unit.data.split(',')[1]
                else:
                    each_measurement.measurement = ''
                    each_measurement.unit = ''

        # Enable/disable Channels
        if form_mod_math.measurements_enabled.data:
            for each_measurement in measurements:
                if each_measurement.unique_id in form_mod_math.measurements_enabled.data:
                    each_measurement.is_enabled = True
                else:
                    each_measurement.is_enabled = False

        original_inputs = mod_math.inputs

        # Collect inputs and measurement name and units
        if mod_math.math_type in ['average',
                                  'redundancy',
                                  'difference',
                                  'statistics',
                                  'verification']:
            if len(form_mod_math.inputs.data) < 2:
                error.append("At least two Inputs must be selected")
            if form_mod_math.inputs.data:
                inputs_joined = ";".join(form_mod_math.inputs.data)
                mod_math.inputs = inputs_joined
            else:
                mod_math.inputs = ''

        if mod_math.math_type == 'average_single':
            mod_math.inputs = form_mod_type.average_input.data

            # Change measurement information
            if form_mod_type.average_input.data and ',' in form_mod_type.average_input.data:
                measurement_id = form_mod_type.average_input.data.split(',')[1]
                selected_measurement = get_measurement(measurement_id)
                if selected_measurement:
                    conversion = Conversion.query.filter(
                        Conversion.unique_id == selected_measurement.conversion_id).first()
                else:
                    conversion = None
                _, unit, measurement = return_measurement_info(
                    selected_measurement, conversion)

                mod_measurement = DeviceMeasurements.query.filter(
                    DeviceMeasurements.device_id == form_mod_math.math_id.data).first()
                mod_measurement.measurement = measurement
                mod_measurement.unit = unit

        elif mod_math.math_type == 'redundancy':
            # If input selection changes, create the default order list that can then be modified
            if original_inputs != ';'.join(form_mod_math.inputs.data):
                mod_math.order_of_use = ';'.join(form_mod_math.inputs.data)
            else:
                # Ensure order_of_use includes all input IDs and is properly formatted
                if mod_math.inputs:
                    mod_math.order_of_use = ';'.join(form_mod_type.order_of_use.data)

        elif mod_math.math_type == 'difference':
            if len(form_mod_math.inputs.data) != 2:
                error.append("Only two Inputs must be selected")
            mod_math.difference_reverse_order = form_mod_type.difference_reverse_order.data
            mod_math.difference_absolute = form_mod_type.difference_absolute.data

        elif mod_math.math_type == 'equation':
            mod_math.equation_input = form_mod_type.equation_input.data
            mod_math.equation = form_mod_type.equation.data

        elif mod_math.math_type == 'humidity':
            mod_math.dry_bulb_t_id = form_mod_type.dry_bulb_temperature.data.split(',')[0]
            mod_math.dry_bulb_t_measure_id = form_mod_type.dry_bulb_temperature.data.split(',')[1]
            dbt_input = Input.query.filter(
                Input.unique_id == mod_math.dry_bulb_t_id).first()
            dbt_math = Input.query.filter(
                Math.unique_id == mod_math.dry_bulb_t_id).first()
            if not dbt_input and not dbt_math:
                error.append("Invalid dry-bulb temperature selection: Must be a valid Input or Math")

            mod_math.wet_bulb_t_id = form_mod_type.wet_bulb_temperature.data.split(',')[0]
            mod_math.wet_bulb_t_measure_id = form_mod_type.wet_bulb_temperature.data.split(',')[1]
            wbt_input = Input.query.filter(
                Input.unique_id == mod_math.wet_bulb_t_id).first()
            wbt_math = Input.query.filter(
                Math.unique_id == mod_math.wet_bulb_t_id).first()
            if not wbt_input and not wbt_math:
                error.append("Invalid wet-bulb temperature selection: Must be a valid Input or Math")

            if form_mod_type.pressure.data:
                mod_math.pressure_pa_id = form_mod_type.pressure.data.split(',')[0]
                mod_math.pressure_pa_measure_id = form_mod_type.pressure.data.split(',')[1]
            else:
                mod_math.pressure_pa_id = None
                mod_math.pressure_pa_measure_id = None

        elif mod_math.math_type == 'verification':
            mod_math.max_difference = form_mod_type.max_difference.data

        elif mod_math.math_type == 'vapor_pressure_deficit':
            mod_math.unique_id_1 = form_mod_type.unique_id_1.data.split(',')[0]
            mod_math.unique_measurement_id_1 = form_mod_type.unique_id_1.data.split(',')[1]
            vpd_input = Input.query.filter(
                Input.unique_id == mod_math.unique_id_1).first()
            vpd_math = Input.query.filter(
                Math.unique_id == mod_math.unique_id_1).first()
            if not vpd_input and not vpd_math:
                error.append("Invalid vapor pressure deficit temperature selection: Must be a valid Input or Math")

            mod_math.unique_id_2 = form_mod_type.unique_id_2.data.split(',')[0]
            mod_math.unique_measurement_id_2 = form_mod_type.unique_id_2.data.split(',')[1]
            vpd_input = Input.query.filter(
                Input.unique_id == mod_math.unique_id_2).first()
            vpd_math = Input.query.filter(
                Math.unique_id == mod_math.unique_id_2).first()
            if not vpd_input and not vpd_math:
                error.append("Invalid vapor pressure deficit humidity selection: Must be a valid Input or Math")

        if not error:
            db.session.commit()
    except Exception as except_msg:
        logger.exception(1)
        error.append(except_msg)

    flash_success_errors(error, action, url_for('routes_page.page_data'))
Esempio n. 10
0
    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()

            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)
                    setpoint, ended = calculate_method_setpoint(
                        self.setpoint_tracking_id,
                        PID,
                        this_pid,
                        Method,
                        MethodData,
                        self.logger)
                    if ended:
                        self.method_start_act = 'Ended'
                    if setpoint is not None:
                        self.setpoint = setpoint
                    else:
                        self.setpoint = self.default_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

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

                    if last_measurement[1] is not None:
                        self.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.setpoint = None

                # If autotune activated, determine control variable (output) from autotune
                if self.autotune_activated:
                    if not self.autotune.run(self.last_measurement):
                        self.control_variable = self.autotune.output

                        if self.autotune_debug:
                            self.logger.info('')
                            self.logger.info("state: {}".format(self.autotune.state))
                            self.logger.info("output: {}".format(self.autotune.output))
                    else:
                        # Autotune has finished
                        timestamp = time.time() - self.autotune_timestamp
                        self.logger.info('')
                        self.logger.info('time:  {0} min'.format(round(timestamp / 60)))
                        self.logger.info('state: {0}'.format(self.autotune.state))

                        if self.autotune.state == PIDAutotune.STATE_SUCCEEDED:
                            for rule in self.autotune.tuning_rules:
                                params = self.autotune.get_pid_parameters(rule)
                                self.logger.info('')
                                self.logger.info('rule: {0}'.format(rule))
                                self.logger.info('Kp: {0}'.format(params.Kp))
                                self.logger.info('Ki: {0}'.format(params.Ki))
                                self.logger.info('Kd: {0}'.format(params.Kd))

                        self.stop_controller(deactivate_pid=True)
                else:
                    # Calculate new control variable (output) from PID Controller

                    # Original PID method
                    self.control_variable = self.update_pid_output(
                        self.last_measurement)

                    # New PID method (untested)
                    # self.control_variable = self.PID_Controller.calc(
                    #     self.last_measurement, self.setpoint)

                self.write_pid_values()  # Write variables to database

        # Is PID in a state that allows manipulation of outputs
        if (self.is_activated and
                self.setpoint is not None and
                (not self.is_paused or self.is_held)):
            self.manipulate_output()
Esempio n. 11
0
def pid_mod(form_mod_pid_base, form_mod_pid_pwm_raise, form_mod_pid_pwm_lower,
            form_mod_pid_output_raise, form_mod_pid_output_lower,
            form_mod_pid_volume_raise, form_mod_pid_volume_lower):
    action = '{action} {controller}'.format(
        action=TRANSLATIONS['modify']['title'],
        controller=TRANSLATIONS['pid']['title'])
    error = []

    dict_outputs = parse_output_information()

    if not form_mod_pid_base.validate():
        error.append(TRANSLATIONS['error']['title'])
        flash_form_errors(form_mod_pid_base)

    mod_pid = PID.query.filter(
        PID.unique_id == form_mod_pid_base.function_id.data).first()

    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.log_level_debug = form_mod_pid_base.log_level_debug.data
    mod_pid.start_offset = form_mod_pid_base.start_offset.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.band = abs(form_mod_pid_base.band.data)
    mod_pid.store_lower_as_negative = form_mod_pid_base.store_lower_as_negative.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
    mod_pid.setpoint_tracking_type = form_mod_pid_base.setpoint_tracking_type.data

    if form_mod_pid_base.setpoint_tracking_type.data == 'method':
        mod_pid.setpoint_tracking_id = form_mod_pid_base.setpoint_tracking_method_id.data
    elif form_mod_pid_base.setpoint_tracking_type.data == 'input-math':
        mod_pid.setpoint_tracking_id = form_mod_pid_base.setpoint_tracking_input_math_id.data
        if form_mod_pid_base.setpoint_tracking_max_age.data:
            mod_pid.setpoint_tracking_max_age = form_mod_pid_base.setpoint_tracking_max_age.data
        else:
            mod_pid.setpoint_tracking_max_age = 120
    else:
        mod_pid.setpoint_tracking_id = ''

    # Change measurement information
    if ',' in form_mod_pid_base.measurement.data:
        measurement_id = form_mod_pid_base.measurement.data.split(',')[1]
        selected_measurement = get_measurement(measurement_id)

        measurements = DeviceMeasurements.query.filter(
            DeviceMeasurements.device_id ==
            form_mod_pid_base.function_id.data).all()
        for each_measurement in measurements:
            # Only set channels 0, 1, 2
            if each_measurement.channel in [0, 1, 2]:
                each_measurement.measurement = selected_measurement.measurement
                each_measurement.unit = selected_measurement.unit

        #
        # Handle Raise Output Settings
        #
        if form_mod_pid_base.raise_output_id.data:
            raise_output_type = Output.query.filter(
                Output.unique_id ==
                form_mod_pid_base.raise_output_id.data).first().output_type

            def default_raise_output_settings(mod):
                if mod.raise_output_type == 'on_off':
                    mod.raise_min_duration = 0
                    mod.raise_max_duration = 0
                    mod.raise_min_off_duration = 0
                elif mod.raise_output_type == 'pwm':
                    mod.raise_min_duration = 2
                    mod.raise_max_duration = 98
                elif mod.raise_output_type == 'volume':
                    mod.raise_min_duration = 0
                    mod.raise_max_duration = 0
                return mod

            raise_output_id_changed = False
            if mod_pid.raise_output_id != form_mod_pid_base.raise_output_id.data:
                mod_pid.raise_output_id = form_mod_pid_base.raise_output_id.data
                raise_output_id_changed = True

            # Output ID changed
            if ('output_types' in dict_outputs[raise_output_type]
                    and mod_pid.raise_output_id and raise_output_id_changed):

                if len(dict_outputs[raise_output_type]['output_types']) == 1:
                    mod_pid.raise_output_type = dict_outputs[
                        raise_output_type]['output_types'][0]
                else:
                    mod_pid.raise_output_type = None

                mod_pid = default_raise_output_settings(mod_pid)

            # Output ID unchanged
            elif ('output_types' in dict_outputs[raise_output_type]
                  and mod_pid.raise_output_id and not raise_output_id_changed):

                if (not mod_pid.raise_output_type or mod_pid.raise_output_type
                        != form_mod_pid_base.raise_output_type.data):
                    if len(dict_outputs[raise_output_type]
                           ['output_types']) > 1:
                        mod_pid.raise_output_type = form_mod_pid_base.raise_output_type.data
                    mod_pid = default_raise_output_settings(mod_pid)
                elif mod_pid.raise_output_type == 'on_off':
                    if not form_mod_pid_output_raise.validate():
                        error.append(TRANSLATIONS['error']['title'])
                        flash_form_errors(form_mod_pid_output_raise)
                    else:
                        mod_pid.raise_min_duration = form_mod_pid_output_raise.raise_min_duration.data
                        mod_pid.raise_max_duration = form_mod_pid_output_raise.raise_max_duration.data
                        mod_pid.raise_min_off_duration = form_mod_pid_output_raise.raise_min_off_duration.data
                elif mod_pid.raise_output_type == 'pwm':
                    if not form_mod_pid_pwm_raise.validate():
                        error.append(TRANSLATIONS['error']['title'])
                        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
                        mod_pid.raise_always_min_pwm = form_mod_pid_pwm_raise.raise_always_min_pwm.data
                elif mod_pid.raise_output_type == 'volume':
                    if not form_mod_pid_volume_raise.validate():
                        error.append(TRANSLATIONS['error']['title'])
                        flash_form_errors(form_mod_pid_volume_raise)
                    else:
                        mod_pid.raise_min_duration = form_mod_pid_volume_raise.raise_min_amount.data
                        mod_pid.raise_max_duration = form_mod_pid_volume_raise.raise_max_amount.data
        else:
            mod_pid.raise_output_id = None

        #
        # Handle Lower Output Settings
        #
        if form_mod_pid_base.lower_output_id.data:
            lower_output_type = Output.query.filter(
                Output.unique_id ==
                form_mod_pid_base.lower_output_id.data).first().output_type

            def default_lower_output_settings(mod):
                if mod.lower_output_type == 'on_off':
                    mod.lower_min_duration = 0
                    mod.lower_max_duration = 0
                    mod.lower_min_off_duration = 0
                elif mod.lower_output_type == 'pwm':
                    mod.lower_min_duration = 2
                    mod.lower_max_duration = 98
                elif mod.lower_output_type == 'volume':
                    mod.lower_min_duration = 0
                    mod.lower_max_duration = 0
                return mod

            lower_output_id_changed = False
            if mod_pid.lower_output_id != form_mod_pid_base.lower_output_id.data:
                mod_pid.lower_output_id = form_mod_pid_base.lower_output_id.data
                lower_output_id_changed = True

            # Output ID changed
            if ('output_types' in dict_outputs[lower_output_type]
                    and mod_pid.lower_output_id and lower_output_id_changed):

                if len(dict_outputs[lower_output_type]['output_types']) == 1:
                    mod_pid.lower_output_type = dict_outputs[
                        lower_output_type]['output_types'][0]
                else:
                    mod_pid.lower_output_type = None

                mod_pid = default_lower_output_settings(mod_pid)

            # Output ID unchanged
            elif ('output_types' in dict_outputs[lower_output_type]
                  and mod_pid.lower_output_id and not lower_output_id_changed):

                if (not mod_pid.lower_output_type or mod_pid.lower_output_type
                        != form_mod_pid_base.lower_output_type.data):
                    if len(dict_outputs[lower_output_type]
                           ['output_types']) > 1:
                        mod_pid.lower_output_type = form_mod_pid_base.lower_output_type.data
                    mod_pid = default_lower_output_settings(mod_pid)
                elif mod_pid.lower_output_type == 'on_off':
                    if not form_mod_pid_output_lower.validate():
                        error.append(TRANSLATIONS['error']['title'])
                        flash_form_errors(form_mod_pid_output_lower)
                    else:
                        mod_pid.lower_min_duration = form_mod_pid_output_lower.lower_min_duration.data
                        mod_pid.lower_max_duration = form_mod_pid_output_lower.lower_max_duration.data
                        mod_pid.lower_min_off_duration = form_mod_pid_output_lower.lower_min_off_duration.data
                elif mod_pid.lower_output_type == 'pwm':
                    if not form_mod_pid_pwm_lower.validate():
                        error.append(TRANSLATIONS['error']['title'])
                        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
                        mod_pid.lower_always_min_pwm = form_mod_pid_pwm_lower.lower_always_min_pwm.data
                elif mod_pid.lower_output_type == 'volume':
                    if not form_mod_pid_volume_lower.validate():
                        error.append(TRANSLATIONS['error']['title'])
                        flash_form_errors(form_mod_pid_volume_lower)
                    else:
                        mod_pid.lower_min_duration = form_mod_pid_volume_lower.lower_min_amount.data
                        mod_pid.lower_max_duration = form_mod_pid_volume_lower.lower_max_amount.data
        else:
            mod_pid.lower_output_id = None

    if (mod_pid.raise_output_id and mod_pid.lower_output_id
            and mod_pid.raise_output_id == mod_pid.lower_output_id):
        error.append(gettext("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.function_id.data)
                flash(
                    "PID Controller settings refresh response: "
                    "{resp}".format(resp=return_value), "success")
    except Exception as except_msg:
        error.append(except_msg)
    flash_success_errors(error, action, url_for('routes_page.page_function'))
Esempio n. 12
0
    def loop(self):
        if self.timer_loop > time.time():
            return

        while self.timer_loop < time.time():
            self.timer_loop += self.period

        temp_c = None
        hum_percent = None
        vpd_pa = None

        last_measurement_temp = self.get_last_measurement(
            self.select_measurement_temperature_c_device_id,
            self.select_measurement_temperature_c_measurement_id,
            max_age=self.max_measure_age_temperature_c)

        self.logger.debug("Temp: {}".format(last_measurement_temp))

        if last_measurement_temp:
            device_measurement = get_measurement(
                self.select_measurement_temperature_c_measurement_id)
            conversion = db_retrieve_table_daemon(
                Conversion, unique_id=device_measurement.conversion_id)
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)
            temp_c = convert_from_x_to_y_unit(unit, 'C',
                                              last_measurement_temp[1])

        last_measurement_hum = self.get_last_measurement(
            self.select_measurement_humidity_device_id,
            self.select_measurement_humidity_measurement_id,
            max_age=self.max_measure_age_humidity)

        self.logger.debug("Hum: {}".format(last_measurement_hum))

        if last_measurement_hum:
            device_measurement = get_measurement(
                self.select_measurement_humidity_measurement_id)
            conversion = db_retrieve_table_daemon(
                Conversion, unique_id=device_measurement.conversion_id)
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)
            hum_percent = convert_from_x_to_y_unit(unit, 'percent',
                                                   last_measurement_hum[1])

        if temp_c and hum_percent:
            measurement_dict = copy.deepcopy(measurements_dict)

            try:
                vpd_pa = calculate_vapor_pressure_deficit(temp_c, hum_percent)
            except TypeError as err:
                self.logger.error("Error: {msg}".format(msg=err))

            if vpd_pa:
                dev_measurement = self.channels_measurement[0]
                channel, unit, measurement = return_measurement_info(
                    dev_measurement, self.channels_conversion[0])

                vpd_store = convert_from_x_to_y_unit('Pa', unit, vpd_pa)

                measurement_dict[0] = {
                    'measurement': measurement,
                    'unit': unit,
                    'value': vpd_store
                }

            # Add measurement(s) to influxdb
            if measurement_dict:
                self.logger.debug(
                    "Adding measurements to InfluxDB with ID {}: {}".format(
                        self.unique_id, measurement_dict))
                add_measurements_influxdb(self.unique_id, measurement_dict)
            else:
                self.logger.debug(
                    "No measurements to add to InfluxDB with ID {}".format(
                        self.unique_id))
        else:
            self.logger.debug(
                "Could not acquire both temperature and humidity measurements."
            )
Esempio n. 13
0
    def loop(self):
        if self.timer_loop > time.time():
            return

        while self.timer_loop < time.time():
            self.timer_loop += self.period

        device_measurement = get_measurement(
            self.select_measurement_measurement_id)

        if not device_measurement:
            self.logger.error("Could not find Device Measurement")
            return

        past_measurements = self.get_past_measurements(
            self.select_measurement_device_id,
            self.select_measurement_measurement_id,
            max_age=self.max_measure_age)

        self.logger.debug(
            "Past Measurements returned: {}".format(past_measurements))

        if not past_measurements:
            self.logger.error(
                "Could not find measurements within the set Max Age")
            return False

        measure = []
        for each_measure in past_measurements:
            measure.append(each_measure[1])

        if len(measure) > 1:
            stat_mean = float(sum(measure) / float(len(measure)))
            stat_median = median(measure)
            stat_minimum = min(measure)
            stat_maximum = max(measure)
            stdev_ = stdev(measure)
            stdev_mean_upper = stat_mean + stdev_
            stdev_mean_lower = stat_mean - stdev_

            list_measurement = [
                stat_mean, stat_median, stat_minimum, stat_maximum, stdev_,
                stdev_mean_upper, stdev_mean_lower
            ]

            for each_channel, each_measurement in self.channels_measurement.items(
            ):
                if each_measurement.is_enabled:
                    channel, unit, measurement = return_measurement_info(
                        each_measurement,
                        self.channels_conversion[each_channel])

                    self.logger.debug(
                        "Saving {} to channel {} with measurement {} and "
                        "unit {}".format(list_measurement[each_channel],
                                         each_channel, measurement, unit))

                    write_influxdb_value(self.unique_id,
                                         unit,
                                         value=list_measurement[each_channel],
                                         measure=measurement,
                                         channel=each_channel)
        else:
            self.logger.debug(
                "Less than 2 measurements found within Max Age. "
                "Calculations need at least 2 measurements. Not calculating.")
Esempio n. 14
0
    def loop(self):
        if self.timer_loop > time.time():
            return

        while self.timer_loop < time.time():
            self.timer_loop += self.period

        measurements = []
        for each_id_set in self.select_measurement:
            device_device_id = each_id_set.split(",")[0]
            device_measure_id = each_id_set.split(",")[1]

            device_measurement = get_measurement(device_measure_id)

            if not device_measurement:
                self.logger.error("Could not find Device Measurement")
                return

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

            self.logger.debug("Dev: {}, unit: {}, channel: {}, measurement: {}, max age:; {}".format(
                device_device_id,
                unit,
                channel,
                measurement,
                self.max_measure_age))

            last_measurement = read_influxdb_single(
                device_device_id,
                unit,
                channel,
                measure=measurement,
                duration_sec=self.max_measure_age,
                value='LAST')

            self.logger.debug("last_measurement: {}".format(last_measurement))

            if not last_measurement:
                self.logger.error(
                    "One more more measurements were not within the set Max Age. Not calculating average.")
                return False
            else:
                measurements.append(last_measurement[1])

        average = float(sum(measurements) / float(len(measurements)))

        measurement_dict = {
            0: {
                'measurement': self.channels_measurement[0].measurement,
                'unit': self.channels_measurement[0].unit,
                'value': average
            }
        }

        if measurement_dict:
            self.logger.debug(
                "Adding measurements to InfluxDB with ID {}: {}".format(
                    self.unique_id, measurement_dict))
            add_measurements_influxdb(self.unique_id, measurement_dict)
        else:
            self.logger.debug(
                "No measurements to add to InfluxDB with ID {}".format(
                    self.unique_id))
Esempio n. 15
0
def pid_mod(form_mod_pid_base, form_mod_pid_pwm_raise, form_mod_pid_pwm_lower,
            form_mod_pid_output_raise, form_mod_pid_output_lower):
    action = '{action} {controller}'.format(
        action=TRANSLATIONS['modify']['title'],
        controller=TRANSLATIONS['pid']['title'])
    error = []

    if not form_mod_pid_base.validate():
        error.append(TRANSLATIONS['error']['title'])
        flash_form_errors(form_mod_pid_base)

    mod_pid = PID.query.filter(
        PID.unique_id == form_mod_pid_base.function_id.data).first()

    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.log_level_debug = form_mod_pid_base.log_level_debug.data
    mod_pid.start_offset = form_mod_pid_base.start_offset.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.band = abs(form_mod_pid_base.band.data)
    mod_pid.store_lower_as_negative = form_mod_pid_base.store_lower_as_negative.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
    mod_pid.setpoint_tracking_type = form_mod_pid_base.setpoint_tracking_type.data
    if form_mod_pid_base.setpoint_tracking_type.data == 'method':
        mod_pid.setpoint_tracking_id = form_mod_pid_base.setpoint_tracking_method_id.data
    elif form_mod_pid_base.setpoint_tracking_type.data == 'input-math':
        mod_pid.setpoint_tracking_id = form_mod_pid_base.setpoint_tracking_input_math_id.data
        if form_mod_pid_base.setpoint_tracking_max_age.data:
            mod_pid.setpoint_tracking_max_age = form_mod_pid_base.setpoint_tracking_max_age.data
        else:
            mod_pid.setpoint_tracking_max_age = 120
    else:
        mod_pid.setpoint_tracking_id = ''

    # Change measurement information
    if ',' in form_mod_pid_base.measurement.data:
        measurement_id = form_mod_pid_base.measurement.data.split(',')[1]
        selected_measurement = get_measurement(measurement_id)

        measurements = DeviceMeasurements.query.filter(
            DeviceMeasurements.device_id ==
            form_mod_pid_base.function_id.data).all()
        for each_measurement in measurements:
            # Only set channels 0, 1, 2
            if each_measurement.channel in [0, 1, 2]:
                each_measurement.measurement = selected_measurement.measurement
                each_measurement.unit = selected_measurement.unit

    if form_mod_pid_base.raise_output_id.data:
        raise_output_type = Output.query.filter(
            Output.unique_id ==
            form_mod_pid_base.raise_output_id.data).first().output_type
        if mod_pid.raise_output_id == form_mod_pid_base.raise_output_id.data:
            if raise_output_type in OUTPUTS_PWM:
                if not form_mod_pid_pwm_raise.validate():
                    error.append(TRANSLATIONS['error']['title'])
                    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_output_raise.validate():
                    error.append(TRANSLATIONS['error']['title'])
                    flash_form_errors(form_mod_pid_output_raise)
                else:
                    mod_pid.raise_min_duration = form_mod_pid_output_raise.raise_min_duration.data
                    mod_pid.raise_max_duration = form_mod_pid_output_raise.raise_max_duration.data
                    mod_pid.raise_min_off_duration = form_mod_pid_output_raise.raise_min_off_duration.data
        else:
            if raise_output_type in OUTPUTS_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_output_id = form_mod_pid_base.raise_output_id.data
    else:
        mod_pid.raise_output_id = None

    if form_mod_pid_base.lower_output_id.data:
        lower_output_type = Output.query.filter(
            Output.unique_id ==
            form_mod_pid_base.lower_output_id.data).first().output_type
        if mod_pid.lower_output_id == form_mod_pid_base.lower_output_id.data:
            if lower_output_type in OUTPUTS_PWM:
                if not form_mod_pid_pwm_lower.validate():
                    error.append(gettext("Error in form field(s)"))
                    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_output_lower.validate():
                    error.append(gettext("Error in form field(s)"))
                    flash_form_errors(form_mod_pid_output_lower)
                else:
                    mod_pid.lower_min_duration = form_mod_pid_output_lower.lower_min_duration.data
                    mod_pid.lower_max_duration = form_mod_pid_output_lower.lower_max_duration.data
                    mod_pid.lower_min_off_duration = form_mod_pid_output_lower.lower_min_off_duration.data
        else:
            if lower_output_type in OUTPUTS_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_output_id = form_mod_pid_base.lower_output_id.data
    else:
        mod_pid.lower_output_id = None

    if (mod_pid.raise_output_id and mod_pid.lower_output_id
            and mod_pid.raise_output_id == mod_pid.lower_output_id):
        error.append(gettext("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.function_id.data)
                flash(
                    "PID Controller settings refresh response: "
                    "{resp}".format(resp=return_value), "success")
    except Exception as except_msg:
        error.append(except_msg)
    flash_success_errors(error, action, url_for('routes_page.page_function'))
Esempio n. 16
0
    def loop(self):
        if self.timer_loop > time.time():
            return

        while self.timer_loop < time.time():
            self.timer_loop += self.period

        temp_wet_k = None
        temp_dry_k = None
        pressure_pa = 101325

        if (self.select_measurement_pressure_pa_device_id
                and self.select_measurement_pressure_pa_measurement_id
                and self.max_measure_age_pressure_pa):

            device_measurement = get_measurement(
                self.select_measurement_pressure_pa_measurement_id)
            conversion = db_retrieve_table_daemon(
                Conversion, unique_id=device_measurement.conversion_id)
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)

            last_measurement_pa = self.get_last_measurement(
                self.select_measurement_pressure_pa_device_id,
                self.select_measurement_pressure_pa_measurement_id,
                max_age=self.max_measure_age_pressure_pa)

            if last_measurement_pa:
                pressure_pa = convert_from_x_to_y_unit(unit, 'Pa',
                                                       last_measurement_pa[1])

        last_measurement_wet = self.get_last_measurement(
            self.select_measurement_temp_wet_c_device_id,
            self.select_measurement_temp_wet_c_measurement_id,
            max_age=self.max_measure_age_temp_wet_c)

        if last_measurement_wet:
            device_measurement = get_measurement(
                self.select_measurement_temp_wet_c_measurement_id)
            conversion = db_retrieve_table_daemon(
                Conversion, unique_id=device_measurement.conversion_id)
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)
            temp_wet_k = convert_from_x_to_y_unit(unit, 'K',
                                                  last_measurement_wet[1])

        last_measurement_dry = self.get_last_measurement(
            self.select_measurement_temp_dry_c_device_id,
            self.select_measurement_temp_dry_c_measurement_id,
            max_age=self.max_measure_age_temp_dry_c)

        if last_measurement_dry:
            device_measurement = get_measurement(
                self.select_measurement_temp_dry_c_measurement_id)
            conversion = db_retrieve_table_daemon(
                Conversion, unique_id=device_measurement.conversion_id)
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)
            temp_dry_k = convert_from_x_to_y_unit(unit, 'K',
                                                  last_measurement_dry[1])

        if temp_wet_k and temp_dry_k:
            measurements = copy.deepcopy(measurements_dict)
            psypi = None

            try:
                psypi = SI.state("DBT", temp_dry_k, "WBT", temp_wet_k,
                                 pressure_pa)
            except TypeError as err:
                self.logger.error("TypeError: {msg}".format(msg=err))

            if not psypi:
                self.logger.error(
                    "Could not calculate humidity from wet/dry bulbs")
                return

            percent_relative_humidity = psypi[2] * 100

            # Ensure percent humidity stays within 0 - 100 % range
            if percent_relative_humidity > 100:
                percent_relative_humidity = 100
            elif percent_relative_humidity < 0:
                percent_relative_humidity = 0

            # Dry bulb temperature: psypi[0])
            # Wet bulb temperature: psypi[5])

            specific_enthalpy = float(psypi[1])
            humidity = float(percent_relative_humidity)
            specific_volume = float(psypi[3])
            humidity_ratio = float(psypi[4])

            self.logger.debug("Dry Temp: {dtk}, "
                              "Wet Temp: {wtk}, "
                              "Pressure: {pres},"
                              "Humidity: {rh}".format(dtk=temp_dry_k,
                                                      wtk=temp_wet_k,
                                                      pres=pressure_pa,
                                                      rh=humidity))

            list_measurement = [
                humidity, humidity_ratio, specific_enthalpy, specific_volume
            ]

            for each_channel, each_measurement in self.channels_measurement.items(
            ):
                if each_measurement.is_enabled:
                    channel, unit, measurement = return_measurement_info(
                        each_measurement,
                        self.channels_conversion[each_channel])

                    measurements[channel] = {
                        'measurement': measurement,
                        'unit': unit,
                        'value': list_measurement[channel]
                    }

            # Add measurement(s) to influxdb
            if measurements:
                self.logger.debug(
                    "Adding measurements to InfluxDB with ID {}: {}".format(
                        self.unique_id, measurements))
                add_measurements_influxdb(self.unique_id, measurements)
            else:
                self.logger.debug(
                    "No measurements to add to InfluxDB with ID {}".format(
                        self.unique_id))
        else:
            self.logger.debug(
                "One or more temperature measurements could not be found within the Max Age. Not calculating."
            )
Esempio n. 17
0
    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()
Esempio n. 18
0
    def get_measurement(self):
        """Gets the sensor's pH measurement."""
        if not self.atlas_device.setup:
            self.logger.error(
                "Error 101: Device not set up. See https://kizniche.github.io/Mycodo/Error-Codes#error-101 for more info."
            )
            return

        ph = None
        self.return_dict = copy.deepcopy(measurements_dict)

        # Compensate measurement based on a temperature measurement
        if self.temperature_comp_meas_measurement_id and self.atlas_command:
            self.logger.debug("pH sensor set to calibrate temperature")

            last_measurement = self.get_last_measurement(
                self.temperature_comp_meas_device_id,
                self.temperature_comp_meas_measurement_id,
                max_age=self.max_age)

            if last_measurement and len(last_measurement) > 1:
                device_measurement = get_measurement(
                    self.temperature_comp_meas_measurement_id)
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=device_measurement.conversion_id)
                _, unit, _ = return_measurement_info(device_measurement,
                                                     conversion)

                if unit != "C":
                    out_value = convert_from_x_to_y_unit(
                        unit, "C", last_measurement[1])
                else:
                    out_value = last_measurement[1]

                self.logger.debug(
                    "Latest temperature used to calibrate: {temp}".format(
                        temp=out_value))
                ret_value, ret_msg = self.atlas_command.calibrate(
                    'temperature', set_amount=out_value)
                time.sleep(0.5)
                self.logger.debug("Calibration returned: {val}, {msg}".format(
                    val=ret_value, msg=ret_msg))
            else:
                self.logger.error(
                    "Calibration measurement not found within the past {} seconds"
                    .format(self.max_age))

        # Read device
        atlas_status, atlas_return = self.atlas_device.query('R')
        self.logger.debug("Device Returned: {}: {}".format(
            atlas_status, atlas_return))

        if atlas_status == 'error':
            self.logger.error(
                "Sensor read unsuccessful: {err}".format(err=atlas_return))
            return

        # Parse device return data
        if self.interface in ['FTDI', 'UART']:
            # Find float value in list
            float_value = None
            for each_split in atlas_return:
                if str_is_float(each_split):
                    float_value = each_split
                    break

            if 'check probe' in atlas_return:
                self.logger.error('"check probe" returned from sensor')
            elif str_is_float(float_value):
                ph = float(float_value)
                self.logger.debug('Found float value: {val}'.format(val=ph))
            else:
                self.logger.error(
                    'Value or "check probe" not found in list: {val}'.format(
                        val=atlas_return))

        elif self.interface == 'I2C':
            if ',' in atlas_return and str_is_float(
                    atlas_return.split(',')[2]):
                ph = float(atlas_return.split(',')[2])
            elif str_is_float(atlas_return):
                ph = float(atlas_return)
            else:
                self.logger.error(
                    "Could not determine pH from returned value: '{}'".format(
                        atlas_return))

        self.value_set(0, ph)

        return self.return_dict
Esempio n. 19
0
def pid_mod(form_mod_pid_base,
            form_mod_pid_pwm_raise, form_mod_pid_pwm_lower,
            form_mod_pid_output_raise, form_mod_pid_output_lower):
    action = '{action} {controller}'.format(
        action=TRANSLATIONS['modify']['title'],
        controller=TRANSLATIONS['pid']['title'])
    error = []

    if not form_mod_pid_base.validate():
        error.append(TRANSLATIONS['error']['title'])
        flash_form_errors(form_mod_pid_base)

    mod_pid = PID.query.filter(
        PID.unique_id == form_mod_pid_base.function_id.data).first()

    # Check if a specific setting can be modified if the PID is active
    if mod_pid.is_activated:
        error = can_set_output(
            error,
            form_mod_pid_base.function_id.data,
            form_mod_pid_base.raise_output_id.data,
            form_mod_pid_base.lower_output_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.start_offset = form_mod_pid_base.start_offset.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.band = abs(form_mod_pid_base.band.data)
    mod_pid.store_lower_as_negative = form_mod_pid_base.store_lower_as_negative.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
    mod_pid.method_id = form_mod_pid_base.method_id.data

    # Change measurement information
    if ',' in form_mod_pid_base.measurement.data:
        measurement_id = form_mod_pid_base.measurement.data.split(',')[1]
        selected_measurement = get_measurement(measurement_id)

        measurements = DeviceMeasurements.query.filter(
            DeviceMeasurements.device_id == form_mod_pid_base.function_id.data).all()
        for each_measurement in measurements:
            # Only set channels 0, 1, 2
            if each_measurement.channel in [0, 1, 2]:
                each_measurement.measurement = selected_measurement.measurement
                each_measurement.unit = selected_measurement.unit

    if form_mod_pid_base.raise_output_id.data:
        raise_output_type = Output.query.filter(
            Output.unique_id == form_mod_pid_base.raise_output_id.data).first().output_type
        if mod_pid.raise_output_id == form_mod_pid_base.raise_output_id.data:
            if raise_output_type in ['pwm', 'command_pwm']:
                if not form_mod_pid_pwm_raise.validate():
                    error.append(TRANSLATIONS['error']['title'])
                    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_output_raise.validate():
                    error.append(TRANSLATIONS['error']['title'])
                    flash_form_errors(form_mod_pid_output_raise)
                else:
                    mod_pid.raise_min_duration = form_mod_pid_output_raise.raise_min_duration.data
                    mod_pid.raise_max_duration = form_mod_pid_output_raise.raise_max_duration.data
                    mod_pid.raise_min_off_duration = form_mod_pid_output_raise.raise_min_off_duration.data
        else:
            if raise_output_type in ['pwm', 'command_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_output_id = form_mod_pid_base.raise_output_id.data
    else:
        mod_pid.raise_output_id = None

    if form_mod_pid_base.lower_output_id.data:
        lower_output_type = Output.query.filter(
            Output.unique_id == form_mod_pid_base.lower_output_id.data).first().output_type
        if mod_pid.lower_output_id == form_mod_pid_base.lower_output_id.data:
            if lower_output_type in ['pwm', 'command_pwm']:
                if not form_mod_pid_pwm_lower.validate():
                    error.append(gettext("Error in form field(s)"))
                    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_output_lower.validate():
                    error.append(gettext("Error in form field(s)"))
                    flash_form_errors(form_mod_pid_output_lower)
                else:
                    mod_pid.lower_min_duration = form_mod_pid_output_lower.lower_min_duration.data
                    mod_pid.lower_max_duration = form_mod_pid_output_lower.lower_max_duration.data
                    mod_pid.lower_min_off_duration = form_mod_pid_output_lower.lower_min_off_duration.data
        else:
            if lower_output_type in ['pwm', 'command_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_output_id = form_mod_pid_base.lower_output_id.data
    else:
        mod_pid.lower_output_id = None

    if (mod_pid.raise_output_id and mod_pid.lower_output_id and
            mod_pid.raise_output_id == mod_pid.lower_output_id):
        error.append(gettext("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.function_id.data)
                flash("PID Controller settings refresh response: "
                      "{resp}".format(resp=return_value), "success")
    except Exception as except_msg:
        error.append(except_msg)
    flash_success_errors(error, action, url_for('routes_page.page_function'))
Esempio n. 20
0
    def get_measurement(self):
        """ Gets the sensor's pH measurement """
        if not self.atlas_device.setup:
            self.logger.error("Input not set up")
            return

        ph = None
        self.return_dict = copy.deepcopy(measurements_dict)

        # Compensate measurement based on a temperature measurement
        if self.temperature_comp_meas_measurement_id and self.atlas_command:
            self.logger.debug("pH sensor set to calibrate temperature")

            device_measurement = get_measurement(
                self.temperature_comp_meas_measurement_id)
            conversion = db_retrieve_table_daemon(
                Conversion, unique_id=device_measurement.conversion_id)
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)

            last_measurement = self.get_last_measurement(
                self.temperature_comp_meas_device_id,
                self.temperature_comp_meas_measurement_id,
                max_age=self.max_age)

            out_value = convert_from_x_to_y_unit(unit, "C",
                                                 last_measurement[1])

            if last_measurement:
                self.logger.debug(
                    "Latest temperature used to calibrate: {temp}".format(
                        temp=out_value))
                ret_value, ret_msg = self.atlas_command.calibrate(
                    'temperature', set_amount=out_value)
                time.sleep(0.5)
                self.logger.debug("Calibration returned: {val}, {msg}".format(
                    val=ret_value, msg=ret_msg))
            else:
                self.logger.error(
                    "Calibration measurement not found within the past {} seconds"
                    .format(self.max_age))

        # Read sensor via FTDI or UART
        if self.interface in ['FTDI', 'UART']:
            ph_status, ph_list = self.atlas_device.query('R')
            if ph_list:
                self.logger.debug(
                    "Returned list: {lines}".format(lines=ph_list))

            # Find float value in list
            float_value = None
            for each_split in ph_list:
                if str_is_float(each_split):
                    float_value = each_split
                    break

            if 'check probe' in ph_list:
                self.logger.error('"check probe" returned from sensor')
            elif str_is_float(float_value):
                ph = float(float_value)
                self.logger.debug('Found float value: {val}'.format(val=ph))
            else:
                self.logger.error(
                    'Value or "check probe" not found in list: {val}'.format(
                        val=ph_list))

        # Read sensor via I2C
        elif self.interface == 'I2C':
            ph_status, ph_str = self.atlas_device.query('R')
            if ph_status == 'error':
                self.logger.error(
                    "Sensor read unsuccessful: {err}".format(err=ph_str))
            elif ph_status == 'success':
                if ',' in ph_str and str_is_float(ph_str.split(',')[2]):
                    ph = float(ph_str.split(',')[2])
                elif str_is_float(ph_str):
                    ph = float(ph_str)
                else:
                    self.logger.error(
                        "Could not determine pH from returned string: '{}'".
                        format(ph_str))

        self.value_set(0, ph)

        return self.return_dict