Exemple #1
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))
Exemple #2
0
    def setup_lcd_line(self, display_id, line, device_id, measurement_id):
        if measurement_id == 'output':
            device_measurement = db_retrieve_table_daemon(
                Output, unique_id=device_id)
        elif measurement_id in ['BLANK', 'IP']:
            device_measurement = None
        else:
            device_measurement = db_retrieve_table_daemon(
                DeviceMeasurements, unique_id=measurement_id)

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

        self.lcd_line[display_id][line]['setup'] = False
        self.lcd_line[display_id][line]['id'] = device_id
        self.lcd_line[display_id][line]['name'] = None
        self.lcd_line[display_id][line]['unit'] = unit
        self.lcd_line[display_id][line]['measure'] = measurement
        self.lcd_line[display_id][line]['channel'] = channel

        if 'time' in measurement_id:
            self.lcd_line[display_id][line]['measure'] = 'time'
        elif measurement_id in ['BLANK', 'IP']:
            self.lcd_line[display_id][line]['measure'] = measurement_id
            self.lcd_line[display_id][line]['name'] = ''

        if not device_id:
            return

        if unit in self.dict_units:
            self.lcd_line[display_id][line]['unit'] = unit
        else:
            self.lcd_line[display_id][line]['unit'] = ''

        # Determine the name
        controllers = [
            Output,
            PID,
            Input,
            Math
        ]
        for each_controller in controllers:
            controller_found = db_retrieve_table_daemon(each_controller, unique_id=device_id)
            if controller_found:
                self.lcd_line[display_id][line]['name'] = controller_found.name

        if (self.lcd_line[display_id][line]['measure'] in ['BLANK', 'IP', 'time'] or
                None not in [self.lcd_line[display_id][line]['name'],
                             self.lcd_line[display_id][line]['unit']]):
            self.lcd_line[display_id][line]['setup'] = True
Exemple #3
0
    def return_single_conversion_info(self):
        """ Return channel, unit, and measurement of a math device measurement """
        math_dev_measurement = self.device_measurements.filter(
            DeviceMeasurements.channel == 0).first()
        if math_dev_measurement:
            conversion = db_retrieve_table_daemon(
                Conversion, unique_id=math_dev_measurement.conversion_id)
        else:
            conversion = None
        channel, unit, measurement = return_measurement_info(
            math_dev_measurement, conversion)

        return channel, unit, measurement
Exemple #4
0
    def write_pid_values(self):
        """ Write PID values to the measurement database """
        if self.PID_Controller.band:
            setpoint_band_lower = self.PID_Controller.setpoint - self.PID_Controller.band
            setpoint_band_upper = self.PID_Controller.setpoint + self.PID_Controller.band
        else:
            setpoint_band_lower = None
            setpoint_band_upper = None

        list_measurements = [
            self.PID_Controller.setpoint,
            setpoint_band_lower,
            setpoint_band_upper,
            self.PID_Controller.P_value,
            self.PID_Controller.I_value,
            self.PID_Controller.D_value
        ]

        measurement_dict = {}
        measurements = self.device_measurements.filter(
            DeviceMeasurements.device_id == self.unique_id).all()
        for each_channel, each_measurement in enumerate(measurements):
            if (each_measurement.channel not in measurement_dict and
                    each_measurement.channel < len(list_measurements)):

                # If setpoint, get unit from PID measurement
                if each_measurement.measurement_type == 'setpoint':
                    setpoint_pid = db_retrieve_table_daemon(
                        PID, unique_id=each_measurement.device_id)
                    if setpoint_pid and ',' in setpoint_pid.measurement:
                        pid_measurement = setpoint_pid.measurement.split(',')[1]
                        setpoint_measurement = db_retrieve_table_daemon(
                            DeviceMeasurements, unique_id=pid_measurement)
                        if setpoint_measurement:
                            conversion = db_retrieve_table_daemon(
                                Conversion, unique_id=setpoint_measurement.conversion_id)
                            _, unit, _ = return_measurement_info(
                                setpoint_measurement, conversion)
                            measurement_dict[each_channel] = {
                                'measurement': each_measurement.measurement,
                                'unit': unit,
                                'value': list_measurements[each_channel]
                            }
                else:
                    measurement_dict[each_channel] = {
                        'measurement': each_measurement.measurement,
                        'unit': each_measurement.unit,
                        'value': list_measurements[each_channel]
                    }

        add_measurements_influxdb(self.unique_id, measurement_dict)
Exemple #5
0
    def write_pid_values(self):
        """ Write PID values to the measurement database """
        if self.band:
            setpoint_band_lower = self.setpoint - self.band
            setpoint_band_upper = self.setpoint + self.band
        else:
            setpoint_band_lower = None
            setpoint_band_upper = None

        list_measurements = [
            self.setpoint,
            setpoint_band_lower,
            setpoint_band_upper,
            self.P_value,
            self.I_value,
            self.D_value
        ]

        measurement_dict = {}
        measurements = self.device_measurements.filter(
            DeviceMeasurements.device_id == self.pid_id).all()
        for each_channel, each_measurement in enumerate(measurements):
            if (each_measurement.channel not in measurement_dict and
                    each_measurement.channel < len(list_measurements)):

                # If setpoint, get unit from PID measurement
                if each_measurement.measurement_type == 'setpoint':
                    setpoint_pid = db_retrieve_table_daemon(
                        PID, unique_id=each_measurement.device_id)
                    if setpoint_pid and ',' in setpoint_pid.measurement:
                        pid_measurement = setpoint_pid.measurement.split(',')[1]
                        setpoint_measurement = db_retrieve_table_daemon(
                            DeviceMeasurements, unique_id=pid_measurement)
                        if setpoint_measurement:
                            conversion = db_retrieve_table_daemon(
                                Conversion, unique_id=setpoint_measurement.conversion_id)
                            _, unit, _ = return_measurement_info(
                                setpoint_measurement, conversion)
                            measurement_dict[each_channel] = {
                                'measurement': each_measurement.measurement,
                                'unit': unit,
                                'value': list_measurements[each_channel]
                            }
                else:
                    measurement_dict[each_channel] = {
                        'measurement': each_measurement.measurement,
                        'unit': each_measurement.unit,
                        'value': list_measurements[each_channel]
                    }

        add_measurements_influxdb(self.pid_id, measurement_dict)
Exemple #6
0
    def setup_output(self):
        self.setup_on_off_output(OUTPUT_INFORMATION)

        if not self.options_channels['pwm_command'][0]:
            self.logger.error("Output must have Python Code set")
            return

        try:
            self.save_output_python_pwm_code(self.unique_id)
            file_run_pwm = '{}/output_pwm_{}.py'.format(PATH_PYTHON_CODE_USER, self.unique_id)

            module_name = "mycodo.output.{}".format(os.path.basename(file_run_pwm).split('.')[0])
            spec = importlib.util.spec_from_file_location(module_name, file_run_pwm)
            output_run_pwm = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(output_run_pwm)
            self.output_run_python_pwm = output_run_pwm.OutputRun(self.logger, self.unique_id)

            self.output_setup = True

            if self.options_channels['state_startup'][0] == 0:
                self.output_switch('off')
            elif self.options_channels['state_startup'][0] == 'set_duty_cycle':
                self.output_switch('on', amount=self.options_channels['startup_value'][0])
            elif self.options_channels['state_startup'][0] == 'last_duty_cycle':
                device_measurement = db_retrieve_table_daemon(DeviceMeasurements).filter(
                    and_(DeviceMeasurements.device_id == self.unique_id,
                         DeviceMeasurements.channel == 0)).first()

                last_measurement = None
                if device_measurement:
                    channel, unit, measurement = return_measurement_info(device_measurement, None)
                    last_measurement = read_last_influxdb(
                        self.unique_id,
                        unit,
                        channel,
                        measure=measurement,
                        duration_sec=None)

                if last_measurement:
                    self.logger.info(
                        "Setting startup duty cycle to last known value of {dc} %".format(
                            dc=last_measurement[1]))
                    self.output_switch('on', amount=last_measurement[1])
                else:
                    self.logger.error(
                        "Output instructed at startup to be set to "
                        "the last known duty cycle, but a last known "
                        "duty cycle could not be found in the measurement "
                        "database")
        except Exception:
            self.logger.exception("Could not set up output")
Exemple #7
0
def form_pid_choices(choices, each_pid, dict_units, dict_measurements):
    device_measurements = DeviceMeasurements.query.filter(
        DeviceMeasurements.device_id == each_pid.unique_id).all()

    for each_measure in device_measurements:
        conversion = Conversion.query.filter(
            Conversion.unique_id == each_measure.conversion_id).first()
        channel, unit, measurement = return_measurement_info(
            each_measure, conversion)

        value = '{input_id},{meas_id}'.format(
            input_id=each_pid.unique_id,
            meas_id=each_measure.unique_id)

        if unit:
            display_unit = find_name_unit(
                dict_units, unit)
            display_measurement = find_name_measurement(
                dict_measurements, measurement)
        elif each_measure.measurement_type == 'setpoint':
            display_unit = None
            display_measurement = 'Setpoint'
        else:
            display_unit = None
            display_measurement = None

        if each_measure.name:
            channel_info = 'CH{cnum} ({cname})'.format(
                cnum=channel + 1, cname=each_measure.name)
        else:
            channel_info = 'CH{cnum}'.format(cnum=channel + 1)

        if display_measurement and display_unit:
            measurement_unit = '{meas} ({unit})'.format(
                meas=display_measurement, unit=display_unit)
        elif display_measurement:
            measurement_unit = '{meas}'.format(
                meas=display_measurement)
        else:
            measurement_unit = '({unit})'.format(unit=display_unit)

        display = '[PID {id:02d}] {i_name} {chan} {meas}'.format(
            id=each_pid.id,
            i_name=each_pid.name,
            chan=channel_info,
            meas=measurement_unit)
        choices.update({value: display})

    return choices
Exemple #8
0
def form_input_choices(choices, each_input, dict_units, dict_measurements):
    device_measurements = DeviceMeasurements.query.filter(
        DeviceMeasurements.device_id == each_input.unique_id).all()

    for each_measure in device_measurements:
        conversion = Conversion.query.filter(
            Conversion.unique_id == each_measure.conversion_id).first()
        channel, unit, measurement = return_measurement_info(
            each_measure, conversion)

        if unit:
            value = '{input_id},{meas_id}'.format(
                input_id=each_input.unique_id,
                meas_id=each_measure.unique_id)

            display_unit = find_name_unit(
                dict_units, unit)
            display_measurement = find_name_measurement(
                dict_measurements, measurement)

            if isinstance(channel, int):
                channel_num = ' CH{cnum}'.format(cnum=channel + 1)
            else:
                channel_num = ''

            if each_measure.name:
                channel_name = ' ({name})'.format(name=each_measure.name)
            else:
                channel_name = ''

            if display_measurement and display_unit:
                measurement_unit = ' {meas} ({unit})'.format(
                    meas=display_measurement, unit=display_unit)
            elif display_measurement:
                measurement_unit = ' {meas}'.format(
                    meas=display_measurement)
            else:
                measurement_unit = ' ({unit})'.format(unit=display_unit)

            display = '[Input {id:02d}] {i_name}{chan_num}{chan_name}{meas}'.format(
                id=each_input.id,
                i_name=each_input.name,
                chan_num=channel_num,
                chan_name=channel_name,
                meas=measurement_unit)

            choices.update({value: display})

    return choices
Exemple #9
0
def form_pid_choices(choices, each_pid, dict_units, dict_measurements):
    device_measurements = DeviceMeasurements.query.filter(
        DeviceMeasurements.device_id == each_pid.unique_id).all()

    for each_measure in device_measurements:
        conversion = Conversion.query.filter(
            Conversion.unique_id == each_measure.conversion_id).first()
        channel, unit, measurement = return_measurement_info(
            each_measure, conversion)

        value = '{input_id},{meas_id}'.format(
            input_id=each_pid.unique_id,
            meas_id=each_measure.unique_id)

        if unit:
            display_unit = find_name_unit(
                dict_units, unit)
            display_measurement = find_name_measurement(
                dict_measurements, measurement)
        elif each_measure.measurement_type == 'setpoint':
            display_unit = None
            display_measurement = 'Setpoint'
        else:
            display_unit = None
            display_measurement = None

        if each_measure.name:
            channel_info = 'CH{cnum} ({cname})'.format(
                cnum=channel + 1, cname=each_measure.name)
        else:
            channel_info = 'CH{cnum}'.format(cnum=channel + 1)

        if display_measurement and display_unit:
            measurement_unit = '{meas} ({unit})'.format(
                meas=display_measurement, unit=display_unit)
        elif display_measurement:
            measurement_unit = '{meas}'.format(
                meas=display_measurement)
        else:
            measurement_unit = '({unit})'.format(unit=display_unit)

        display = '[PID {id:02d}] {i_name} {chan} {meas}'.format(
            id=each_pid.id,
            i_name=each_pid.name,
            chan=channel_info,
            meas=measurement_unit)
        choices.update({value: display})

    return choices
Exemple #10
0
    def return_single_measure_info(self):
        math_dev_measurement = self.device_measurements.filter(
            DeviceMeasurements.channel == 0).first()
        math_conversion = db_retrieve_table_daemon(
            Conversion, unique_id=math_dev_measurement.conversion_id)
        _, math_unit, _ = return_measurement_info(math_dev_measurement,
                                                  math_conversion)

        device_id = self.inputs.split(',')[0]
        measurement_id = self.inputs.split(',')[1]

        device_measurement = db_retrieve_table_daemon(DeviceMeasurements,
                                                      unique_id=measurement_id)
        if device_measurement:
            measure_conversion = db_retrieve_table_daemon(
                Conversion, unique_id=device_measurement.conversion_id)
        else:
            measure_conversion = None
        (measure_channel, measure_unit,
         measure_measurement) = return_measurement_info(
             device_measurement, measure_conversion)

        return (device_id, math_dev_measurement, math_unit, measure_channel,
                measure_unit, measure_measurement)
    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

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

        sum_measurements = sum_past_seconds(
            self.select_measurement_device_id,
            unit,
            channel,
            self.max_measure_age,
            measure=measurement)

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

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

        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))
    def get_last_measurement(device_id, measurement_id, max_age=None):
        device_measurement = db_retrieve_table_daemon(
            DeviceMeasurements).filter(
                DeviceMeasurements.unique_id == measurement_id).first()
        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)

        last_measurement = read_last_influxdb(device_id, unit, measurement,
                                              channel, max_age)

        return last_measurement
Exemple #13
0
def form_input_choices(choices, each_input, dict_units, dict_measurements):
    device_measurements = DeviceMeasurements.query.filter(
        DeviceMeasurements.device_id == each_input.unique_id).all()

    for each_measure in device_measurements:
        conversion = Conversion.query.filter(
            Conversion.unique_id == each_measure.conversion_id).first()
        channel, unit, measurement = return_measurement_info(
            each_measure, conversion)

        if unit:
            value = '{input_id},{meas_id}'.format(
                input_id=each_input.unique_id,
                meas_id=each_measure.unique_id)

            display_unit = find_name_unit(
                dict_units, unit)
            display_measurement = find_name_measurement(
                dict_measurements, measurement)

            if each_measure.name:
                channel_name = ' ({name})'.format(name=each_measure.name)
            else:
                channel_name = ''

            channel_info = "CH{cnum}{cname}".format(
                cnum=channel + 1, cname=channel_name)

            if display_measurement and display_unit:
                measurement_unit = '{meas} ({unit})'.format(
                    meas=display_measurement, unit=display_unit)
            elif display_measurement:
                measurement_unit = '{meas}'.format(
                    meas=display_measurement)
            else:
                measurement_unit = '({unit})'.format(unit=display_unit)

            display = '[Input {id:02d}] {i_name} {chan} {meas}'.format(
                id=each_input.id,
                i_name=each_input.name,
                chan=channel_info,
                meas=measurement_unit)

            choices.update({value: display})

    return choices
Exemple #14
0
def get_condition_value_dict(condition_id):
    """
    Returns dict of multiple condition measurements for Conditional controllers
    :param condition_id: Conditional condition ID
    :return: measurement: dict of float measurements
    """
    # Check Measurement Conditions
    sql_condition = db_retrieve_table_daemon(ConditionalConditions).filter(
        ConditionalConditions.unique_id == condition_id).first()

    if sql_condition.condition_type == 'measurement_dict':
        device_id = sql_condition.measurement.split(',')[0]
        measurement_id = sql_condition.measurement.split(',')[1]
        max_age = sql_condition.max_age

        device_measurement = db_retrieve_table_daemon(DeviceMeasurements,
                                                      unique_id=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)

        if None in [channel, unit]:
            logger.error(
                "Could not determine channel or unit from measurement ID: "
                "{}".format(measurement_id))
            return

        past_measurements_dict = get_past_measurements(device_id,
                                                       measurement_id,
                                                       max_age=max_age)

        # TODO: Change to return dictionary in next major release
        string_ts_values = ''
        if past_measurements_dict:
            string_ts_values = ''
            for index, each_set in enumerate(past_measurements_dict):
                string_ts_values += '{},{}'.format(each_set[0], each_set[1])
                if index + 1 < len(past_measurements_dict):
                    string_ts_values += ';'

        return string_ts_values
Exemple #15
0
    def get_measurements_from_id(self, device_id, measure_id):
        device_measurement = get_measurement(measure_id)
        if not device_measurement:
            return False, None

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

        measure = read_last_influxdb(device_id,
                                     unit,
                                     channel,
                                     measure=measurement,
                                     duration_sec=self.max_measure_age)
        if not measure:
            return False, None
        return True, measure
def form_math_choices(choices, each_math, dict_units, dict_measurements):
    device_measurements = DeviceMeasurements.query.filter(
        DeviceMeasurements.device_id == each_math.unique_id).all()

    for each_measure in device_measurements:
        conversion = Conversion.query.filter(
            Conversion.unique_id == each_measure.conversion_id).first()
        channel, unit, measurement = return_measurement_info(
            each_measure, conversion)

        if unit:
            value = '{input_id},{meas_id}'.format(
                input_id=each_math.unique_id,
                meas_id=each_measure.unique_id)

            display_unit = find_name_unit(
                dict_units, unit)
            display_measurement = find_name_measurement(
                dict_measurements, measurement)

            if each_measure.name:
                channel_info = 'CH{cnum} ({cname})'.format(
                    cnum=channel, cname=each_measure.name)
            else:
                channel_info = 'CH{cnum}'.format(cnum=channel)

            if display_measurement and display_unit:
                measurement_unit = '{meas} ({unit})'.format(
                    meas=display_measurement, unit=display_unit)
            elif display_measurement:
                measurement_unit = '{meas}'.format(
                    meas=display_measurement)
            else:
                measurement_unit = '({unit})'.format(unit=display_unit)

            display = '[Math {id:02d}] {i_name} {chan} {meas}'.format(
                id=each_math.id,
                i_name=each_math.name,
                chan=channel_info,
                meas=measurement_unit)
            choices.append({'value': value, 'item': display})

    return choices
Exemple #17
0
    def initialize(self):
        self.setup_output_variables(OUTPUT_INFORMATION)

        if self.options_channels['pwm_command'][0]:
            self.output_setup = True

            if self.options_channels['state_startup'][0] == 0:
                self.output_switch('off')
            elif self.options_channels['state_startup'][0] == 'set_duty_cycle':
                self.output_switch(
                    'on', amount=self.options_channels['startup_value'][0])
            elif self.options_channels['state_startup'][
                    0] == 'last_duty_cycle':
                device_measurement = db_retrieve_table_daemon(
                    DeviceMeasurements).filter(
                        and_(DeviceMeasurements.device_id == self.unique_id,
                             DeviceMeasurements.channel == 0)).first()

                last_measurement = None
                if device_measurement:
                    channel, unit, measurement = return_measurement_info(
                        device_measurement, None)
                    last_measurement = read_influxdb_single(
                        self.unique_id,
                        unit,
                        channel,
                        measure=measurement,
                        value='LAST')

                if last_measurement:
                    self.logger.info(
                        "Setting startup duty cycle to last known value of {dc} %"
                        .format(dc=last_measurement[1]))
                    self.output_switch('on', amount=last_measurement[1])
                else:
                    self.logger.error(
                        "Output instructed at startup to be set to "
                        "the last known duty cycle, but a last known "
                        "duty cycle could not be found in the measurement "
                        "database")
        else:
            self.logger.error("Output must have command set")
Exemple #18
0
def get_condition_measurement(sql_condition):
    device_id = sql_condition.measurement.split(',')[0]
    measurement_id = sql_condition.measurement.split(',')[1]

    device_measurement = db_retrieve_table_daemon(DeviceMeasurements,
                                                  unique_id=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)

    if None in [channel, unit]:
        logger.error(
            "Could not determine channel or unit from measurement ID: "
            "{}".format(measurement_id))
        return

    max_age = sql_condition.max_age

    # Check Measurement Conditions
    if sql_condition.condition_type == 'measurement':
        # Check if there hasn't been a measurement in the last set number
        # of seconds. If not, trigger conditional
        last_measurement = get_last_measurement(device_id, unit, measurement,
                                                channel, max_age)
        return last_measurement

    # If the edge detection variable is set, calling this function will
    # trigger an edge detection event. This will merely produce the correct
    # message based on the edge detection settings.
    elif sql_condition.condition_type == 'gpio_state':
        try:
            GPIO.setmode(GPIO.BCM)
            GPIO.setup(int(sql_condition.gpio_pin), GPIO.IN)
            gpio_state = GPIO.input(int(sql_condition.gpio_pin))
        except:
            gpio_state = None
            logger.error("Exception reading the GPIO pin")
        return gpio_state
def get_condition_measurement(sql_condition):
    device_id = sql_condition.measurement.split(',')[0]
    measurement_id = sql_condition.measurement.split(',')[1]

    device_measurement = db_retrieve_table_daemon(
        DeviceMeasurements, unique_id=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)

    if None in [channel, unit]:
        logger.error(
            "Could not determine channel or unit from measurement ID: "
            "{}".format(measurement_id))
        return

    max_age = sql_condition.max_age

    # Check Measurement Conditions
    if sql_condition.condition_type == 'measurement':
        # Check if there hasn't been a measurement in the last set number
        # of seconds. If not, trigger conditional
        last_measurement = get_last_measurement(
            device_id, unit, measurement, channel, max_age)
        return last_measurement

    # If the edge detection variable is set, calling this function will
    # trigger an edge detection event. This will merely produce the correct
    # message based on the edge detection settings.
    elif sql_condition.condition_type == 'gpio_state':
        try:
            GPIO.setmode(GPIO.BCM)
            GPIO.setup(int(sql_condition.gpio_pin), GPIO.IN)
            gpio_state = GPIO.input(int(sql_condition.gpio_pin))
        except:
            gpio_state = None
            logger.error("Exception reading the GPIO pin")
        return gpio_state
Exemple #20
0
    def math_redundancy(self, measurement_dict):
        list_order = self.order_of_use.split(';')
        measurement_success = False

        for each_id_measurement_id in list_order:
            device_id = each_id_measurement_id.split(',')[0]
            measurement_id = each_id_measurement_id.split(',')[1]

            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            try:
                success_measure, measure = self.get_measurements_from_id(
                    device_id, measurement_id)

                if success_measure:
                    measurement_dict = {
                        channel: {
                            'measurement': measurement,
                            'unit': unit,
                            'value': float(measure[1]),
                            'timestamp_utc': measure[0],
                        }
                    }
                    measurement_success = True
                    break

            except Exception as msg:
                self.logger.exception(
                    "redundancy Error: {err}".format(err=msg))
        if not measurement_success:
            self.error_not_within_max_age()
        return measurement_dict
Exemple #21
0
    def math_average(self, measurement_dict):
        math_dev_measurement = self.device_measurements.filter(
            DeviceMeasurements.channel == 0).first()
        channel, unit, measurement = return_measurement_info(
            math_dev_measurement, None)

        success, measure = self.get_measurements_from_str(self.inputs)
        if success:
            average = float(sum(measure) / float(len(measure)))

            measurement_dict = {
                channel: {
                    'measurement': measurement,
                    'unit': unit,
                    'value': average
                }
            }
        elif measure:
            self.logger.error(measure)
        else:
            self.error_not_within_max_age()
        return measurement_dict
Exemple #22
0
    def get_temp_data(self):
        """Get the temperature."""
        if self.temperature_comp_meas_measurement_id:
            self.logger.debug("Temperature corrections will be applied")

            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: {temp} C".format(temp=out_value))
            else:
                self.logger.error(
                    "Temperature measurement not found within the "
                    "past {} seconds".format(self.max_age))
                out_value = None

        else:
            self.logger.debug("No temperature corrections applied")
            out_value = None

        return out_value
def get_condition_value_dict(condition_id):
    """
    Returns dict of multiple condition measurements for Conditional controllers
    :param condition_id: Conditional condition ID
    :return: measurement: dict of float measurements
    """
    # Check Measurement Conditions
    sql_condition = db_retrieve_table_daemon(ConditionalConditions).filter(
        ConditionalConditions.unique_id == condition_id).first()

    if sql_condition.condition_type == 'measurement_dict':
        device_id = sql_condition.measurement.split(',')[0]
        measurement_id = sql_condition.measurement.split(',')[1]

        device_measurement = db_retrieve_table_daemon(
            DeviceMeasurements, unique_id=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)

        if None in [channel, unit]:
            logger.error(
                "Could not determine channel or unit from measurement ID: "
                "{}".format(measurement_id))
            return

        max_age = sql_condition.max_age
        # Check if there hasn't been a measurement in the last set number
        # of seconds. If not, trigger conditional
        past_measurements = get_past_measurements(
            device_id, unit, measurement, channel, max_age)
        return past_measurements
Exemple #24
0
def last_data_pid(pid_id, input_period):
    """Return the most recent time and value from influxdb."""
    if not current_user.is_authenticated:
        return "You are not logged in and cannot access this endpoint"
    if not str_is_float(input_period):
        return '', 204

    try:
        pid = PID.query.filter(PID.unique_id == pid_id).first()

        if len(pid.measurement.split(',')) == 2:
            device_id = pid.measurement.split(',')[0]
            measurement_id = pid.measurement.split(',')[1]
        else:
            device_id = None
            measurement_id = None

        actual_measurement = DeviceMeasurements.query.filter(
            DeviceMeasurements.unique_id == measurement_id).first()
        if actual_measurement:
            actual_conversion = Conversion.query.filter(
                Conversion.unique_id ==
                actual_measurement.conversion_id).first()
        else:
            actual_conversion = None

        (actual_channel, actual_unit,
         actual_measurement) = return_measurement_info(actual_measurement,
                                                       actual_conversion)

        setpoint_unit = None
        if pid and ',' in pid.measurement:
            pid_measurement = pid.measurement.split(',')[1]
            setpoint_measurement = DeviceMeasurements.query.filter(
                DeviceMeasurements.unique_id == pid_measurement).first()
            if setpoint_measurement:
                conversion = Conversion.query.filter(
                    Conversion.unique_id ==
                    setpoint_measurement.conversion_id).first()
                _, setpoint_unit, _ = return_measurement_info(
                    setpoint_measurement, conversion)

        p_value = return_point_timestamp(pid_id,
                                         'pid_value',
                                         input_period,
                                         measurement='pid_p_value')
        i_value = return_point_timestamp(pid_id,
                                         'pid_value',
                                         input_period,
                                         measurement='pid_i_value')
        d_value = return_point_timestamp(pid_id,
                                         'pid_value',
                                         input_period,
                                         measurement='pid_d_value')
        if None not in (p_value[1], i_value[1], d_value[1]):
            pid_value = [
                p_value[0],
                f'{float(p_value[1]) + float(i_value[1]) + float(d_value[1]):.3f}'
            ]
        else:
            pid_value = None

        setpoint_band = None
        if pid.band:
            try:
                daemon = DaemonControl()
                setpoint_band = daemon.pid_get(pid.unique_id, 'setpoint_band')
            except:
                logger.debug("Couldn't get setpoint")

        live_data = {
            'activated':
            pid.is_activated,
            'paused':
            pid.is_paused,
            'held':
            pid.is_held,
            'setpoint':
            return_point_timestamp(pid_id,
                                   setpoint_unit,
                                   input_period,
                                   channel=0),
            'setpoint_band':
            setpoint_band,
            'pid_p_value':
            p_value,
            'pid_i_value':
            i_value,
            'pid_d_value':
            d_value,
            'pid_pid_value':
            pid_value,
            'duration_time':
            return_point_timestamp(pid_id,
                                   's',
                                   input_period,
                                   measurement='duration_time'),
            'duty_cycle':
            return_point_timestamp(pid_id,
                                   'percent',
                                   input_period,
                                   measurement='duty_cycle'),
            'actual':
            return_point_timestamp(device_id,
                                   actual_unit,
                                   input_period,
                                   measurement=actual_measurement,
                                   channel=actual_channel)
        }
        return jsonify(live_data)
    except KeyError:
        logger.debug("No Data returned form influxdb")
        return '', 204
    except Exception as err:
        logger.exception(f"URL for 'last_pid' raised and error: {err}")
        return '', 204
Exemple #25
0
    def all_outputs_set_state(self):
        """Turn all outputs on that are set to be on at startup"""
        for each_output_id in self.output_unique_id:
            try:
                if self.output_state_startup[each_output_id] is None:
                    pass  # Don't turn on or off

                # PWM Outputs
                elif self.output_type[each_output_id] in self.output_types[
                        'pwm']:
                    if self.output_state_startup[each_output_id] == '0':
                        self.logger.info(
                            "Setting Output {id} startup duty cycle to 0 %".
                            format(id=each_output_id.split('-')[0]))
                        self.output_on_off(
                            each_output_id,
                            'off',
                            trigger_conditionals=self.
                            trigger_functions_at_start[each_output_id])

                    elif self.output_state_startup[
                            each_output_id] == 'set_duty_cycle':
                        self.logger.info(
                            "Setting Output {id} startup duty cycle to user-set value of {dc} %"
                            .format(
                                id=each_output_id.split('-')[0],
                                dc=self.output_startup_value[each_output_id]))
                        self.output_on_off(
                            each_output_id,
                            'on',
                            output_type='pwm',
                            amount=self.output_startup_value[each_output_id],
                            trigger_conditionals=self.
                            trigger_functions_at_start[each_output_id])

                    elif self.output_state_startup[
                            each_output_id] == 'last_duty_cycle':
                        device_measurement = db_retrieve_table_daemon(
                            DeviceMeasurements).filter(
                                DeviceMeasurements.device_id ==
                                each_output_id).all()
                        measurement = None
                        for each_meas in device_measurement:
                            if each_meas.measurement == 'duty_cycle':
                                measurement = each_meas
                        channel, unit, measurement = return_measurement_info(
                            measurement, None)

                        last_measurement = read_last_influxdb(
                            each_output_id,
                            unit,
                            channel,
                            measure=measurement,
                            duration_sec=None)

                        if last_measurement:
                            self.logger.info(
                                "Setting Output {id} startup duty cycle to last known value of {dc} %"
                                .format(id=each_output_id.split('-')[0],
                                        dc=last_measurement[1]))
                            self.output_on_off(
                                each_output_id,
                                'on',
                                output_type='pwm',
                                amount=last_measurement[1],
                                trigger_conditionals=self.
                                trigger_functions_at_start[each_output_id])
                        else:
                            self.logger.error(
                                "Output {id} instructed at startup to be set to "
                                "the last known duty cycle, but a last known "
                                "duty cycle could not be found in the measurement "
                                "database".format(
                                    id=each_output_id.split('-')[0]))

                # Non-PWM outputs
                elif self.output_state_startup[each_output_id] == '1':
                    self.logger.info(
                        "Setting Output {id} startup state to ON".format(
                            id=each_output_id.split('-')[0]))
                    self.output_on_off(
                        each_output_id,
                        'on',
                        trigger_conditionals=self.
                        trigger_functions_at_start[each_output_id])

                elif self.output_state_startup[each_output_id] == '0':
                    self.logger.info(
                        "Setting Output {id} startup state to OFF".format(
                            id=each_output_id.split('-')[0]))
                    self.output_on_off(each_output_id,
                                       'off',
                                       trigger_conditionals=False)
            except:
                self.logger.error(
                    "Could not set state for output {}".format(each_output_id))
Exemple #26
0
    def calculate_math(self):
        measurement_dict = {}

        if self.math_type == 'average':

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()

            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)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                average = float(sum(measure) / float(len(measure)))

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': average
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'average_single':

            device_id = self.inputs.split(',')[0]
            measurement_id = self.inputs.split(',')[1]

            device_measurement = db_retrieve_table_daemon(
                DeviceMeasurements, unique_id=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)

            try:
                last_measurements = read_past_influxdb(
                    device_id,
                    unit,
                    measurement,
                    channel,
                    self.max_measure_age)

                if last_measurements:
                    measure_list = []
                    for each_set in last_measurements:
                        if len(each_set) == 2:
                            measure_list.append(each_set[1])
                    average = sum(measure_list) / float(len(measure_list))

                    measurement_dict = {
                        channel: {
                            'measurement': measurement,
                            'unit': unit,
                            'value': average
                        }
                    }
                else:
                    self.error_not_within_max_age()
            except Exception as msg:
                self.logger.exception("average_single Error: {err}".format(err=msg))

        elif self.math_type == 'difference':

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            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)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                if self.difference_reverse_order:
                    difference = measure[1] - measure[0]
                else:
                    difference = measure[0] - measure[1]
                if self.difference_absolute:
                    difference = abs(difference)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': difference
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'equation':

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            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)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                replaced_str = self.equation.replace('x', str(measure[0]))
                equation_output = eval(replaced_str)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': float(equation_output)
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'redundancy':
            list_order = self.order_of_use.split(';')
            measurement_success = False

            for each_id_measurement_id in list_order:
                device_id = each_id_measurement_id.split(',')[0]
                measurement_id = each_id_measurement_id.split(',')[1]

                device_measurement = self.device_measurements.filter(
                    DeviceMeasurements.channel == 0).first()
                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)

                try:
                    success_measure, measure = self.get_measurements_from_id(
                        device_id, measurement_id)

                    if success_measure:
                        measurement_dict = {
                            channel: {
                                'measurement': measurement,
                                'unit': unit,
                                'value': float(measure[1]),
                                'timestamp': measure[0],
                            }
                        }
                        measurement_success = True
                        break

                except Exception as msg:
                    self.logger.exception("redundancy Error: {err}".format(err=msg))

            if not measurement_success:
                self.error_not_within_max_age()

        elif self.math_type == 'statistics':

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                # Perform some math
                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_measurement in self.device_measurements.all():
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=each_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        each_measurement, conversion)

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

            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'verification':

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            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)

            success, measure = self.get_measurements_from_str(self.inputs)
            if (success and
                    max(measure) - min(measure) <
                    self.max_difference):
                difference = max(measure) - min(measure)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': difference
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'humidity':

            pressure_pa = 101325
            critical_error = False

            if self.pressure_pa_id and self.pressure_pa_measure_id:
                success_pa, pressure = self.get_measurements_from_id(
                    self.pressure_pa_id, self.pressure_pa_measure_id)
                if success_pa:
                    pressure_pa = int(pressure[1])
                    # Pressure must be in Pa, convert if not

                    if db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.pressure_pa_measure_id):
                        measurement = db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.pressure_pa_measure_id)
                    else:
                        self.logger.error("Could not find pressure measurement")
                        measurement = None
                        critical_error = True

                    if measurement and measurement.unit != 'Pa':
                        for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                            if (each_conv.convert_unit_from == measurement.unit and
                                    each_conv.convert_unit_to == 'Pa'):
                                pressure_pa = convert_units(
                                    each_conv.unique_id, pressure_pa)
                            else:
                                self.logger.error(
                                    "Could not find conversion for unit "
                                    "{unit} to Pa (Pascals)".format(
                                        unit=measurement.unit))
                                critical_error = True

            success_dbt, dry_bulb_t = self.get_measurements_from_id(
                self.dry_bulb_t_id, self.dry_bulb_t_measure_id)
            success_wbt, wet_bulb_t = self.get_measurements_from_id(
                self.wet_bulb_t_id, self.wet_bulb_t_measure_id)

            if success_dbt and success_wbt:
                dbt_kelvin = float(dry_bulb_t[1])
                wbt_kelvin = float(wet_bulb_t[1])

                if db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id):
                    measurement = db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id)
                else:
                    self.logger.error("Could not find pressure measurement")
                    measurement = None
                    critical_error = True

                if measurement and measurement.unit != 'K':
                    conversion_found = False
                    for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                        if (each_conv.convert_unit_from == measurement.unit and
                                each_conv.convert_unit_to == 'K'):
                            dbt_kelvin = convert_units(
                                each_conv.unique_id, dbt_kelvin)
                            conversion_found = True
                    if not conversion_found:
                        self.logger.error(
                            "Could not find conversion for unit "
                            "{unit} to K (Kelvin)".format(
                                unit=measurement.unit))
                        critical_error = True

                if db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id):
                    measurement = db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id)
                else:
                    self.logger.error("Could not find pressure measurement")
                    measurement = None
                    critical_error = True

                if measurement and measurement.unit != 'K':
                    conversion_found = False
                    for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                        if (each_conv.convert_unit_from == measurement.unit and
                                each_conv.convert_unit_to == 'K'):
                            wbt_kelvin = convert_units(
                                each_conv.unique_id, wbt_kelvin)
                            conversion_found = True
                    if not conversion_found:
                        self.logger.error(
                            "Could not find conversion for unit "
                            "{unit} to K (Kelvin)".format(
                                unit=measurement.unit))
                        critical_error = True

                # Convert temperatures to Kelvin (already done above)
                # dbt_kelvin = celsius_to_kelvin(dry_bulb_t_c)
                # wbt_kelvin = celsius_to_kelvin(wet_bulb_t_c)
                psypi = None

                try:
                    if not critical_error:
                        psypi = SI.state(
                            "DBT", dbt_kelvin, "WBT", wbt_kelvin, pressure_pa)
                    else:
                        self.logger.error(
                            "One or more critical errors prevented the "
                            "humidity from being calculated")
                except TypeError as err:
                    self.logger.error("TypeError: {msg}".format(msg=err))

                if psypi:
                    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])

                    list_measurement = [
                        specific_enthalpy,
                        humidity,
                        specific_volume,
                        humidity_ratio
                    ]

                    for each_measurement in self.device_measurements.all():
                        conversion = db_retrieve_table_daemon(
                            Conversion, unique_id=each_measurement.conversion_id)
                        channel, unit, measurement = return_measurement_info(
                            each_measurement, conversion)

                        measurement_dict[channel] = {
                            'measurement': measurement,
                            'unit': unit,
                            'value': list_measurement[channel]
                        }
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'vapor_pressure_deficit':

            vpd_pa = None
            critical_error = False

            success_dbt, temperature = self.get_measurements_from_id(
                self.unique_id_1, self.unique_measurement_id_1)
            success_wbt, humidity = self.get_measurements_from_id(
                self.unique_id_2, self.unique_measurement_id_2)

            if success_dbt and success_wbt:
                vpd_temperature_celsius = float(temperature[1])
                vpd_humidity_percent = float(humidity[1])

                if db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.unique_measurement_id_1):
                    measurement = db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.unique_measurement_id_1)
                else:
                    self.logger.error("Could not find temperature measurement")
                    measurement = None
                    critical_error = True

                if measurement and measurement.unit != 'C':
                    conversion_found = False
                    for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                        if (each_conv.convert_unit_from == measurement.unit and
                                each_conv.convert_unit_to == 'C'):
                            vpd_temperature_celsius = convert_units(
                                each_conv.unique_id, vpd_temperature_celsius)
                            conversion_found = True
                    if not conversion_found:
                        self.logger.error(
                            "Could not find conversion for unit "
                            "{unit} to C (Celsius)".format(
                                unit=measurement.unit))
                        critical_error = True

                if db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.unique_measurement_id_2):
                    measurement = db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.unique_measurement_id_2)
                else:
                    self.logger.error("Could not find humidity measurement")
                    measurement = None
                    critical_error = True

                if measurement and measurement.unit != 'percent':
                    conversion_found = False
                    for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                        if (each_conv.convert_unit_from == measurement.unit and
                                each_conv.convert_unit_to == 'percent'):
                            vpd_humidity_percent = convert_units(
                                each_conv.unique_id, vpd_humidity_percent)
                            conversion_found = True
                    if not conversion_found:
                        self.logger.error(
                            "Could not find conversion for unit "
                            "{unit} to percent (%)".format(
                                unit=measurement.unit))
                        critical_error = True

                try:
                    if not critical_error:
                        vpd_pa = calculate_vapor_pressure_deficit(
                            vpd_temperature_celsius, vpd_humidity_percent)
                    else:
                        self.logger.error(
                            "One or more critical errors prevented the "
                            "vapor pressure deficit from being calculated")
                except TypeError as err:
                    self.logger.error("TypeError: {msg}".format(msg=err))

                if vpd_pa:
                    measure = self.device_measurements.first()
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=measure.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        measure, conversion)

                    measurement_dict[channel] = {
                        'measurement': measurement,
                        'unit': unit,
                        'value': vpd_pa
                    }
            else:
                self.error_not_within_max_age()

        else:
            self.logger.error("Unknown math type: {type}".format(type=self.math_type))

        # Finally, add measurements to influxdb
        add_measurements_influxdb(self.unique_id, measurement_dict)
Exemple #27
0
def export_data(unique_id, measurement_id, start_seconds, end_seconds):
    """
    Return data from start_seconds to end_seconds from influxdb.
    Used for exporting data.
    """
    current_app.config['INFLUXDB_USER'] = INFLUXDB_USER
    current_app.config['INFLUXDB_PASSWORD'] = INFLUXDB_PASSWORD
    current_app.config['INFLUXDB_DATABASE'] = INFLUXDB_DATABASE
    current_app.config['INFLUXDB_TIMEOUT'] = 5
    dbcon = influx_db.connection

    output = Output.query.filter(Output.unique_id == unique_id).first()
    input_dev = Input.query.filter(Input.unique_id == unique_id).first()
    math = Math.query.filter(Math.unique_id == unique_id).first()

    if output:
        name = output.name
    elif input_dev:
        name = input_dev.name
    elif math:
        name = math.name
    else:
        name = None

    device_measurement = DeviceMeasurements.query.filter(
        DeviceMeasurements.unique_id == measurement_id).first()
    if device_measurement:
        conversion = Conversion.query.filter(
            Conversion.unique_id == device_measurement.conversion_id).first()
    else:
        conversion = None
    channel, unit, measurement = return_measurement_info(
        device_measurement, conversion)

    utc_offset_timedelta = datetime.datetime.utcnow() - datetime.datetime.now()
    start = datetime.datetime.fromtimestamp(float(start_seconds))
    start += utc_offset_timedelta
    start_str = start.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
    end = datetime.datetime.fromtimestamp(float(end_seconds))
    end += utc_offset_timedelta
    end_str = end.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

    query_str = query_string(
        unit, unique_id,
        measure=measurement, channel=channel,
        start_str=start_str, end_str=end_str)
    if query_str == 1:
        flash('Invalid query string', 'error')
        return redirect(url_for('routes_page.page_export'))
    raw_data = dbcon.query(query_str).raw

    if not raw_data or 'series' not in raw_data:
        flash('No measurements to export in this time period', 'error')
        return redirect(url_for('routes_page.page_export'))

    # Generate column names
    col_1 = 'timestamp (UTC)'
    col_2 = '{name} {meas} ({id})'.format(
        name=name, meas=measurement, id=unique_id)
    csv_filename = '{id}_{meas}.csv'.format(id=unique_id, meas=measurement)

    # Populate list of dictionary entries for each column to convert to CSV
    # and send to the user to download
    csv_data = []
    for each_data in raw_data['series'][0]['values']:
        csv_data.append({col_1: str(each_data[0][:-4]).replace('T', ' '),
                         col_2: each_data[1]})

    return send_csv(csv_data, csv_filename, [col_1, col_2])
Exemple #28
0
    def calculate_math(self):
        measurement_dict = {}

        #
        # Average (multiple channels)
        #
        if self.math_type == 'average':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, None)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                average = float(sum(measure) / float(len(measure)))

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': average
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Average (single channel)
        #
        elif self.math_type == 'average_single':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            math_conversion = db_retrieve_table_daemon(
                Conversion, unique_id=math_dev_measurement.conversion_id)
            (math_channel,
             math_unit,
             math_measurement) = return_measurement_info(
                math_dev_measurement, math_conversion)

            device_id = self.inputs.split(',')[0]
            measurement_id = self.inputs.split(',')[1]

            if measurement_id == 'output':
                output = db_retrieve_table_daemon(Output, unique_id=device_id)
                measure_channel = output.channel
                measure_unit = output.unit
                measure_measurement = output.measurement
            else:
                device_measurement = db_retrieve_table_daemon(
                    DeviceMeasurements, unique_id=measurement_id)
                if device_measurement:
                    measure_conversion = db_retrieve_table_daemon(
                        Conversion,
                        unique_id=device_measurement.conversion_id)
                else:
                    measure_conversion = None
                (measure_channel,
                 measure_unit,
                 measure_measurement) = return_measurement_info(
                    device_measurement, measure_conversion)

            try:
                return_value = average_past_seconds(
                    device_id,
                    measure_unit,
                    measure_channel,
                    self.max_measure_age,
                    measure=measure_measurement)

                if math_dev_measurement.conversion_id:
                    return_value = convert_units(
                        math_dev_measurement.conversion_id,
                        return_value)

                if return_value:
                    measurement_dict = {
                        math_dev_measurement.channel: {
                            'measurement': measure_measurement,
                            'unit': math_unit,
                            'value': return_value
                        }
                    }
                else:
                    self.error_not_within_max_age()
            except Exception as msg:
                self.logger.exception(
                    "average_single Error: {err}".format(err=msg))

        #
        # Sum (multiple channels)
        #
        elif self.math_type == 'sum':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                sum_value = float(sum(measure))

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': sum_value
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Sum (single channel)
        #
        elif self.math_type == 'sum_single':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            math_conversion = db_retrieve_table_daemon(
                Conversion, unique_id=math_dev_measurement.conversion_id)
            (math_channel,
             math_unit,
             math_measurement) = return_measurement_info(
                math_dev_measurement, math_conversion)

            device_id = self.inputs.split(',')[0]
            measurement_id = self.inputs.split(',')[1]

            if measurement_id == 'output':
                output = db_retrieve_table_daemon(Output, unique_id=device_id)
                measure_channel = output.channel
                measure_unit = output.unit
                measure_measurement = output.measurement
            else:
                device_measurement = db_retrieve_table_daemon(
                    DeviceMeasurements, unique_id=measurement_id)
                if device_measurement:
                    measure_conversion = db_retrieve_table_daemon(
                        Conversion,
                        unique_id=device_measurement.conversion_id)
                else:
                    measure_conversion = None
                (measure_channel,
                 measure_unit,
                 measure_measurement) = return_measurement_info(
                    device_measurement, measure_conversion)

            try:
                return_value = sum_past_seconds(
                    device_id,
                    measure_unit,
                    measure_channel,
                    self.max_measure_age,
                    measure=measure_measurement)

                if math_dev_measurement.conversion_id:
                    return_value = convert_units(
                        math_dev_measurement.conversion_id,
                        return_value)

                if return_value:
                    measurement_dict = {
                        math_dev_measurement.channel: {
                            'measurement': measure_measurement,
                            'unit': math_unit,
                            'value': return_value
                        }
                    }
                else:
                    self.error_not_within_max_age()
            except Exception as msg:
                self.logger.exception(
                    "sum_single Error: {err}".format(err=msg))

        #
        # Difference between two channels
        #
        elif self.math_type == 'difference':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                if self.difference_reverse_order:
                    difference = measure[1] - measure[0]
                else:
                    difference = measure[0] - measure[1]
                if self.difference_absolute:
                    difference = abs(difference)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': difference
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Equation (math performed on measurement)
        #
        elif self.math_type == 'equation':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            success, measure = self.get_measurements_from_str(
                self.equation_input)
            if success:
                if 'x' in self.equation:
                    replaced_str = self.equation.replace('x', str(measure[0]))
                else:
                    replaced_str = self.equation
                equation_output = eval(replaced_str)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': float(equation_output)
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Redundancy (Use next measurement if one isn't currently available)
        #
        elif self.math_type == 'redundancy':
            list_order = self.order_of_use.split(';')
            measurement_success = False

            for each_id_measurement_id in list_order:
                device_id = each_id_measurement_id.split(',')[0]
                measurement_id = each_id_measurement_id.split(',')[1]

                math_dev_measurement = self.device_measurements.filter(
                    DeviceMeasurements.channel == 0).first()
                if math_dev_measurement:
                    conversion = db_retrieve_table_daemon(
                        Conversion,
                        unique_id=math_dev_measurement.conversion_id)
                else:
                    conversion = None
                channel, unit, measurement = return_measurement_info(
                    math_dev_measurement, conversion)

                try:
                    success_measure, measure = self.get_measurements_from_id(
                        device_id, measurement_id)

                    if success_measure:
                        measurement_dict = {
                            channel: {
                                'measurement': measurement,
                                'unit': unit,
                                'value': float(measure[1]),
                                'timestamp_utc': measure[0],
                            }
                        }
                        measurement_success = True
                        break

                except Exception as msg:
                    self.logger.exception(
                        "redundancy Error: {err}".format(err=msg))

            if not measurement_success:
                self.error_not_within_max_age()

        #
        # Statistical analysis on all measurements from a period of time
        #
        elif self.math_type == 'statistics':
            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                # Perform some math
                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_measurement in self.device_measurements.all():
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=each_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        each_measurement, conversion)

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

            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Verification (only use measurement if it's close to another measurement)
        #
        elif self.math_type == 'verification':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            success, measure = self.get_measurements_from_str(self.inputs)
            if (success and
                    max(measure) - min(measure) <
                    self.max_difference):
                difference = max(measure) - min(measure)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': difference
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Calculate humidity from wet- and dry-bulb temperatures
        #
        elif self.math_type == 'humidity':
            pressure_pa = 101325
            critical_error = False

            if self.pressure_pa_id and self.pressure_pa_measure_id:

                success_pa, pressure = self.get_measurements_from_id(
                    self.pressure_pa_id, self.pressure_pa_measure_id)

                if success_pa:
                    pressure_pa = int(pressure[1])
                    # Pressure must be in Pa, convert if not

                    measurement_press = db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.pressure_pa_measure_id)
                    if not measurement_press:
                        self.logger.error(
                            "Could not find pressure measurement")
                        measurement_press = None
                        critical_error = True

                    if measurement_press and measurement_press.unit != 'Pa':
                        pressure_pa, status = self.is_measurement_unit(
                            measurement_press.unit, 'Pa', pressure_pa)
                        if status == 'error':
                            critical_error = True

            success_dbt, dry_bulb_t = self.get_measurements_from_id(
                self.dry_bulb_t_id, self.dry_bulb_t_measure_id)
            success_wbt, wet_bulb_t = self.get_measurements_from_id(
                self.wet_bulb_t_id, self.wet_bulb_t_measure_id)

            if success_dbt and success_wbt:
                dbt_kelvin = float(dry_bulb_t[1])
                wbt_kelvin = float(wet_bulb_t[1])

                measurement_db_temp = db_retrieve_table_daemon(
                    DeviceMeasurements,
                    unique_id=self.dry_bulb_t_measure_id)
                if not measurement_db_temp:
                    self.logger.error(
                        "Could not find dry bulb temperature measurement")
                    measurement_db_temp = None
                    critical_error = True

                if measurement_db_temp and measurement_db_temp.unit != 'K':
                    dbt_kelvin, status = self.is_measurement_unit(
                        measurement_db_temp.unit, 'K', dbt_kelvin)
                    if status == 'error':
                        critical_error = True

                measurement_wb_temp = db_retrieve_table_daemon(
                    DeviceMeasurements,
                    unique_id=self.wet_bulb_t_measure_id)
                if not measurement_wb_temp:
                    self.logger.error(
                        "Could not find wet bulb temperature measurement")
                    measurement_wb_temp = None
                    critical_error = True

                if measurement_wb_temp and measurement_wb_temp.unit != 'K':
                    wbt_kelvin, status = self.is_measurement_unit(
                        measurement_wb_temp.unit, 'K', wbt_kelvin)
                    if status == 'error':
                        critical_error = True

                # Convert temperatures to Kelvin (already done above)
                # dbt_kelvin = celsius_to_kelvin(dry_bulb_t_c)
                # wbt_kelvin = celsius_to_kelvin(wet_bulb_t_c)
                psypi = None

                try:
                    if not critical_error:
                        psypi = SI.state(
                            "DBT", dbt_kelvin, "WBT", wbt_kelvin, pressure_pa)
                    else:
                        self.logger.error(
                            "One or more critical errors prevented the "
                            "humidity from being calculated")
                except TypeError as err:
                    self.logger.error("TypeError: {msg}".format(msg=err))

                if psypi:
                    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])

                    list_measurement = [
                        specific_enthalpy,
                        humidity,
                        specific_volume,
                        humidity_ratio
                    ]

                    for each_measurement in self.device_measurements.all():
                        conversion = db_retrieve_table_daemon(
                            Conversion,
                            unique_id=each_measurement.conversion_id)
                        channel, unit, measurement = return_measurement_info(
                            each_measurement, conversion)

                        measurement_dict[channel] = {
                            'measurement': measurement,
                            'unit': unit,
                            'value': list_measurement[channel]
                        }
            else:
                self.error_not_within_max_age()

        #
        # Calculate vapor pressure deficit from temperature and humidity
        #
        elif self.math_type == 'vapor_pressure_deficit':
            vpd_pa = None
            critical_error = False

            success_temp, temperature = self.get_measurements_from_id(
                self.unique_id_1, self.unique_measurement_id_1)
            success_hum, humidity = self.get_measurements_from_id(
                self.unique_id_2, self.unique_measurement_id_2)

            if success_temp and success_hum:
                vpd_temperature_celsius = float(temperature[1])
                vpd_humidity_percent = float(humidity[1])

                measurement_temp = db_retrieve_table_daemon(
                    DeviceMeasurements,
                    unique_id=self.unique_measurement_id_1)
                if not measurement_temp:
                    self.logger.error("Could not find temperature measurement")
                    measurement_temp = None
                    critical_error = True

                if measurement_temp and measurement_temp.unit != 'C':
                    vpd_temperature_celsius, status = self.is_measurement_unit(
                        measurement_temp.unit, 'C', vpd_temperature_celsius)
                    if status == 'error':
                        critical_error = True

                measurement_hum = db_retrieve_table_daemon(
                    DeviceMeasurements,
                    unique_id=self.unique_measurement_id_2)
                if not measurement_hum:
                    self.logger.error("Could not find humidity measurement")
                    measurement_hum = None
                    critical_error = True

                if measurement_hum and measurement_hum.unit != 'percent':
                    vpd_humidity_percent, status = self.is_measurement_unit(
                        measurement_hum.unit, 'percent', vpd_humidity_percent)
                    if status == 'error':
                        critical_error = True

                try:
                    if not critical_error:
                        vpd_pa = calculate_vapor_pressure_deficit(
                            vpd_temperature_celsius, vpd_humidity_percent)
                    else:
                        self.logger.error(
                            "One or more critical errors prevented the "
                            "vapor pressure deficit from being calculated")
                except TypeError as err:
                    self.logger.error("TypeError: {msg}".format(msg=err))

                if vpd_pa:
                    math_dev_measurement = self.device_measurements.first()
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=math_dev_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        math_dev_measurement, conversion)

                    measurement_dict[channel] = {
                        'measurement': measurement,
                        'unit': unit,
                        'value': vpd_pa
                    }
            else:
                self.error_not_within_max_age()

        else:
            self.logger.error("Unknown math type: {type}".format(type=self.math_type))

        # Finally, add measurements 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))
Exemple #29
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'))
def get_condition_value(condition_id):
    """
    Returns condition measurements for Conditional controllers
    :param condition_id: Conditional condition ID
    :return: measurement: multiple types
    """
    sql_condition = db_retrieve_table_daemon(ConditionalConditions).filter(
        ConditionalConditions.unique_id == condition_id).first()

    # Check Measurement Conditions
    if sql_condition.condition_type in ['measurement',
                                        'measurement_past_average',
                                        'measurement_past_sum']:
        device_id = sql_condition.measurement.split(',')[0]
        measurement_id = sql_condition.measurement.split(',')[1]

        device_measurement = db_retrieve_table_daemon(
            DeviceMeasurements, unique_id=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)

        if None in [channel, unit]:
            logger.error(
                "Could not determine channel or unit from measurement ID: "
                "{}".format(measurement_id))
            return

        max_age = sql_condition.max_age

        if sql_condition.condition_type == 'measurement':
            return_measurement = get_last_measurement(
                device_id, unit, measurement, channel, max_age)
        elif sql_condition.condition_type == 'measurement_past_average':
            measurement_list = []
            measurements_str = get_past_measurements(
                device_id, unit, measurement, channel, max_age)
            for each_set in measurements_str.split(';'):
                measurement_list.append(float(each_set.split(',')[1]))
            return_measurement = sum(measurement_list) / len(measurement_list)
        elif sql_condition.condition_type == 'measurement_past_sum':
            measurement_list = []
            measurements_str = get_past_measurements(
                device_id, unit, measurement, channel, max_age)
            for each_set in measurements_str.split(';'):
                measurement_list.append(float(each_set.split(',')[1]))
            return_measurement = sum(measurement_list)
        else:
            return

        return return_measurement

    # Return GPIO state
    elif sql_condition.condition_type == 'gpio_state':
        try:
            import RPi.GPIO as GPIO
            GPIO.setmode(GPIO.BCM)
            GPIO.setup(int(sql_condition.gpio_pin), GPIO.IN)
            gpio_state = GPIO.input(int(sql_condition.gpio_pin))
        except Exception as e:
            gpio_state = None
            logger.error("Exception reading the GPIO pin: {}".format(e))
        return gpio_state

    # Return output state
    elif sql_condition.condition_type == 'output_state':
        control = DaemonControl()
        return control.output_state(sql_condition.output_id)

    # Return the duration the output is currently on for
    elif sql_condition.condition_type == 'output_duration_on':
        control = DaemonControl()
        return control.output_sec_currently_on(sql_condition.output_id)

    # Return controller active state
    elif sql_condition.condition_type == 'controller_status':
        controller_type, _, _ = which_controller(sql_condition.controller_id)
        control = DaemonControl()
        return control.controller_is_active(sql_condition.controller_id)
Exemple #31
0
def past_data(unique_id, measure_type, measurement_id, past_seconds):
    """Return data from past_seconds until present from influxdb"""
    if not str_is_float(past_seconds):
        return '', 204

    if measure_type == 'tag':
        notes_list = []

        tag = NoteTags.query.filter(NoteTags.unique_id == unique_id).first()
        notes = Notes.query.filter(
            Notes.date_time >= (datetime.datetime.utcnow() - datetime.timedelta(seconds=int(past_seconds)))).all()

        for each_note in notes:
            if tag.unique_id in each_note.tags.split(','):
                notes_list.append(
                    [each_note.date_time.strftime("%Y-%m-%dT%H:%M:%S.000000000Z"), each_note.name, each_note.note])

        if notes_list:
            return jsonify(notes_list)
        else:
            return '', 204

    elif measure_type in ['input', 'math', 'output', 'pid']:
        current_app.config['INFLUXDB_USER'] = INFLUXDB_USER
        current_app.config['INFLUXDB_PASSWORD'] = INFLUXDB_PASSWORD
        current_app.config['INFLUXDB_DATABASE'] = INFLUXDB_DATABASE
        current_app.config['INFLUXDB_TIMEOUT'] = 5
        dbcon = influx_db.connection

        if measure_type in ['input', 'math', 'pid']:
            measure = DeviceMeasurements.query.filter(
                DeviceMeasurements.unique_id == measurement_id).first()
        elif measure_type == 'output':
            measure = Output.query.filter(
                Output.unique_id == unique_id).first()
        else:
            measure = None

        if not measure:
            return "Could not find measurement"

        if measure:
            conversion = Conversion.query.filter(
                Conversion.unique_id == measure.conversion_id).first()
        else:
            conversion = None

        channel, unit, measurement = return_measurement_info(
            measure, conversion)

        if hasattr(measure, 'measurement_type') and measure.measurement_type == 'setpoint':
            setpoint_pid = PID.query.filter(PID.unique_id == measure.device_id).first()
            if setpoint_pid and ',' in setpoint_pid.measurement:
                pid_measurement = setpoint_pid.measurement.split(',')[1]
                setpoint_measurement = DeviceMeasurements.query.filter(
                    DeviceMeasurements.unique_id == pid_measurement).first()
                if setpoint_measurement:
                    conversion = Conversion.query.filter(
                        Conversion.unique_id == setpoint_measurement.conversion_id).first()
                    _, unit, measurement = return_measurement_info(setpoint_measurement, conversion)

        try:
            query_str = query_string(
                unit, unique_id,
                measure=measurement,
                channel=channel,
                past_sec=past_seconds)

            if query_str == 1:
                return '', 204

            raw_data = dbcon.query(query_str).raw

            if 'series' in raw_data:
                return jsonify(raw_data['series'][0]['values'])
            else:
                return '', 204
        except Exception as e:
            logger.debug("URL for 'past_data' raised and error: "
                         "{err}".format(err=e))
            return '', 204
Exemple #32
0
def async_data(device_id, device_type, measurement_id, start_seconds, end_seconds):
    """
    Return data from start_seconds to end_seconds from influxdb.
    Used for asynchronous graph display of many points (up to millions).
    """
    if device_type == 'tag':
        notes_list = []
        tag = NoteTags.query.filter(NoteTags.unique_id == device_id).first()

        start = datetime.datetime.utcfromtimestamp(float(start_seconds))
        if end_seconds == '0':
            end = datetime.datetime.utcnow()
        else:
            end = datetime.datetime.utcfromtimestamp(float(end_seconds))

        notes = Notes.query.filter(
            and_(Notes.date_time >= start, Notes.date_time <= end)).all()
        for each_note in notes:
            if tag.unique_id in each_note.tags.split(','):
                notes_list.append(
                    [each_note.date_time.strftime("%Y-%m-%dT%H:%M:%S.000000000Z"), each_note.name, each_note.note])

        if notes_list:
            return jsonify(notes_list)
        else:
            return '', 204

    current_app.config['INFLUXDB_USER'] = INFLUXDB_USER
    current_app.config['INFLUXDB_PASSWORD'] = INFLUXDB_PASSWORD
    current_app.config['INFLUXDB_DATABASE'] = INFLUXDB_DATABASE
    current_app.config['INFLUXDB_TIMEOUT'] = 5
    dbcon = influx_db.connection

    if device_type in ['input', 'math', 'pid']:
        measure = DeviceMeasurements.query.filter(DeviceMeasurements.unique_id == measurement_id).first()
    elif device_type == 'output':
        measure = Output.query.filter(Output.unique_id == device_id).first()
    else:
        measure = None

    if not measure:
        return "Could not find measurement"

    if measure:
        conversion = Conversion.query.filter(
            Conversion.unique_id == measure.conversion_id).first()
    else:
        conversion = None
    channel, unit, measurement = return_measurement_info(
        measure, conversion)

    # Set the time frame to the past year if start/end not specified
    if start_seconds == '0' and end_seconds == '0':
        # Get how many points there are in the past year
        query_str = query_string(
            unit, device_id,
            measure=measurement,
            channel=channel,
            value='COUNT')

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        count_points = raw_data['series'][0]['values'][0][1]
        # Get the timestamp of the first point in the past year
        query_str = query_string(
            unit, device_id,
            measure=measurement,
            channel=channel,
            limit=1)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        first_point = raw_data['series'][0]['values'][0][0]
        end = datetime.datetime.utcnow()
        end_str = end.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
    # Set the time frame to the past start epoch to now
    elif start_seconds != '0' and end_seconds == '0':
        start = datetime.datetime.utcfromtimestamp(float(start_seconds))
        start_str = start.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
        end = datetime.datetime.utcnow()
        end_str = end.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

        query_str = query_string(
            unit, device_id,
            measure=measurement,
            channel=channel,
            value='COUNT',
            start_str=start_str,
            end_str=end_str)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        count_points = raw_data['series'][0]['values'][0][1]
        # Get the timestamp of the first point in the past year

        query_str = query_string(
            unit, device_id,
            measure=measurement,
            channel=channel,
            start_str=start_str,
            end_str=end_str,
            limit=1)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        first_point = raw_data['series'][0]['values'][0][0]
    else:
        start = datetime.datetime.utcfromtimestamp(float(start_seconds))
        start_str = start.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
        end = datetime.datetime.utcfromtimestamp(float(end_seconds))
        end_str = end.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

        query_str = query_string(
            unit, device_id,
            measure=measurement,
            channel=channel,
            value='COUNT',
            start_str=start_str,
            end_str=end_str)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        count_points = raw_data['series'][0]['values'][0][1]
        # Get the timestamp of the first point in the past year

        query_str = query_string(
            unit, device_id,
            measure=measurement,
            channel=channel,
            start_str=start_str,
            end_str=end_str,
            limit=1)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        first_point = raw_data['series'][0]['values'][0][0]

    start = datetime.datetime.strptime(first_point[:26],
                                       '%Y-%m-%dT%H:%M:%S.%f')
    start_str = start.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

    logger.debug('Count = {}'.format(count_points))
    logger.debug('Start = {}'.format(start))
    logger.debug('End   = {}'.format(end))

    # How many seconds between the start and end period
    time_difference_seconds = (end - start).total_seconds()
    logger.debug('Difference seconds = {}'.format(time_difference_seconds))

    # If there are more than 700 points in the time frame, we need to group
    # data points into 700 groups with points averaged in each group.
    if count_points > 700:
        # Average period between input reads
        seconds_per_point = time_difference_seconds / count_points
        logger.debug('Seconds per point = {}'.format(seconds_per_point))

        # How many seconds to group data points in
        group_seconds = int(time_difference_seconds / 700)
        logger.debug('Group seconds = {}'.format(group_seconds))

        try:
            query_str = query_string(
                unit, device_id,
                measure=measurement,
                channel=channel,
                value='MEAN',
                start_str=start_str,
                end_str=end_str,
                group_sec=group_seconds)

            if query_str == 1:
                return '', 204
            raw_data = dbcon.query(query_str).raw

            return jsonify(raw_data['series'][0]['values'])
        except Exception as e:
            logger.error("URL for 'async_data' raised and error: "
                         "{err}".format(err=e))
            return '', 204
    else:
        try:
            query_str = query_string(
                unit, device_id,
                measure=measurement,
                channel=channel,
                start_str=start_str,
                end_str=end_str)

            if query_str == 1:
                return '', 204
            raw_data = dbcon.query(query_str).raw

            return jsonify(raw_data['series'][0]['values'])
        except Exception as e:
            logger.error("URL for 'async_data' raised and error: "
                         "{err}".format(err=e))
            return '', 204
Exemple #33
0
    def math_vapor_pressure_deficit(self, measurement_dict):
        vpd_pa = None
        critical_error = False

        success_temp, temperature = self.get_measurements_from_id(
            self.unique_id_1, self.unique_measurement_id_1)
        success_hum, humidity = self.get_measurements_from_id(
            self.unique_id_2, self.unique_measurement_id_2)

        if success_temp and success_hum:
            vpd_temperature_celsius = float(temperature[1])
            vpd_humidity_percent = float(humidity[1])

            measurement_temp = db_retrieve_table_daemon(
                DeviceMeasurements, unique_id=self.unique_measurement_id_1)
            if not measurement_temp:
                self.logger.error("Could not find temperature measurement")
                measurement_temp = None
                critical_error = True

            if measurement_temp and measurement_temp.unit != 'C':
                vpd_temperature_celsius, status = self.is_measurement_unit(
                    measurement_temp.unit, 'C', vpd_temperature_celsius)
                if status == 'error':
                    critical_error = True

            measurement_hum = db_retrieve_table_daemon(
                DeviceMeasurements, unique_id=self.unique_measurement_id_2)
            if not measurement_hum:
                self.logger.error("Could not find humidity measurement")
                measurement_hum = None
                critical_error = True

            if measurement_hum and measurement_hum.unit != 'percent':
                vpd_humidity_percent, status = self.is_measurement_unit(
                    measurement_hum.unit, 'percent', vpd_humidity_percent)
                if status == 'error':
                    critical_error = True

            try:
                if not critical_error:
                    vpd_pa = calculate_vapor_pressure_deficit(
                        vpd_temperature_celsius, vpd_humidity_percent)
                else:
                    self.logger.error(
                        "One or more critical errors prevented the "
                        "vapor pressure deficit from being calculated")
            except TypeError as err:
                self.logger.error("TypeError: {msg}".format(msg=err))

            if vpd_pa:
                math_dev_measurement = self.device_measurements.first()
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
                channel, unit, measurement = return_measurement_info(
                    math_dev_measurement, conversion)

                measurement_dict[channel] = {
                    'measurement': measurement,
                    'unit': unit,
                    'value': vpd_pa
                }
        else:
            self.error_not_within_max_age()
        return measurement_dict
    def loop(self):
        if self.timer_loop > time.time():
            return

        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.")
Exemple #35
0
def graph_y_axes(dict_measurements):
    """ Determine which y-axes to use for each Graph """
    y_axes = {}

    device_measurements = DeviceMeasurements.query.all()
    graph = Dashboard.query.all()
    input_dev = Input.query.all()
    math = Math.query.all()
    output = Output.query.all()
    pid = PID.query.all()

    devices_list = [input_dev, math, output, pid]

    # Iterate through each Dashboard object
    for each_graph in graph:

        # Iterate through device tables
        for each_device in devices_list:

            if each_device == input_dev:
                dev_and_measure_ids = each_graph.input_ids_measurements.split(';')
            elif each_device == math:
                dev_and_measure_ids = each_graph.math_ids.split(';')
            elif each_device == output:
                dev_and_measure_ids = each_graph.output_ids.split(';')
            elif each_device == pid:
                dev_and_measure_ids = each_graph.pid_ids.split(';')
            else:
                dev_and_measure_ids = []

            # Iterate through each set of ID and measurement of the
            # dashboard element
            for each_id_measure in dev_and_measure_ids:

                if each_device in [input_dev, math, pid] and ',' in each_id_measure:

                    if each_graph.unique_id not in y_axes:
                        y_axes[each_graph.unique_id] = []

                    measure_id = each_id_measure.split(',')[1]

                    for each_measurement in device_measurements:
                        if each_measurement.unique_id == measure_id:

                            unit = None
                            if each_measurement.measurement_type == 'setpoint':
                                setpoint_pid = PID.query.filter(PID.unique_id == each_measurement.device_id).first()
                                if setpoint_pid and ',' in setpoint_pid.measurement:
                                    pid_measurement = setpoint_pid.measurement.split(',')[1]
                                    setpoint_measurement = DeviceMeasurements.query.filter(
                                        DeviceMeasurements.unique_id == pid_measurement).first()
                                    if setpoint_measurement:
                                        conversion = Conversion.query.filter(
                                            Conversion.unique_id == setpoint_measurement.conversion_id).first()
                                        _, unit, measurement = return_measurement_info(setpoint_measurement, conversion)
                            else:
                                conversion = Conversion.query.filter(
                                    Conversion.unique_id == each_measurement.conversion_id).first()
                                _, unit, _ = return_measurement_info(each_measurement, conversion)

                            if unit:
                                if not y_axes[each_graph.unique_id]:
                                    y_axes[each_graph.unique_id] = [unit]
                                elif y_axes[each_graph.unique_id] and unit not in y_axes[each_graph.unique_id]:
                                    y_axes.setdefault(each_graph.unique_id, []).append(unit)

                elif each_device == output and ',' in each_id_measure:
                    if each_graph.unique_id not in y_axes:
                        y_axes[each_graph.unique_id] = []

                    output_id = each_id_measure.split(',')[0]

                    for each_output in output:
                        if each_output.unique_id == output_id:
                            if not y_axes[each_graph.unique_id]:
                                y_axes[each_graph.unique_id] = [each_output.unit]
                            elif y_axes[each_graph.unique_id] and each_output.unit not in y_axes[each_graph.unique_id]:
                                y_axes.setdefault(each_graph.unique_id, []).append(each_output.unit)

                elif len(each_id_measure.split(',')) == 4:
                    if each_graph.unique_id not in y_axes:
                        y_axes[each_graph.unique_id] = []

                    unit = each_id_measure.split(',')[2]

                    if not y_axes[each_graph.unique_id]:
                        y_axes[each_graph.unique_id] = [unit]
                    elif y_axes[each_graph.unique_id] and unit not in y_axes[each_graph.unique_id]:
                        y_axes.setdefault(each_graph.unique_id, []).append(unit)

                elif len(each_id_measure.split(',')) == 2:
                    if each_graph.unique_id not in y_axes:
                        y_axes[each_graph.unique_id] = []

                    unique_id = each_id_measure.split(',')[0]
                    measurement = each_id_measure.split(',')[1]

                    y_axes[each_graph.unique_id] = check_func(
                        each_device,
                        unique_id,
                        y_axes[each_graph.unique_id],
                        measurement,
                        dict_measurements,
                        device_measurements,
                        input_dev,
                        output,
                        math)

                elif len(each_id_measure.split(',')) == 3:
                    if each_graph.unique_id not in y_axes:
                        y_axes[each_graph.unique_id] = []

                    unique_id = each_id_measure.split(',')[0]
                    measurement = each_id_measure.split(',')[1]
                    unit = each_id_measure.split(',')[2]

                    y_axes[each_graph.unique_id] = check_func(
                        each_device,
                        unique_id,
                        y_axes[each_graph.unique_id],
                        measurement,
                        dict_measurements,
                        device_measurements,
                        input_dev,
                        output,
                        math,
                        unit=unit)

    return y_axes
Exemple #36
0
def async_data(device_id, device_type, measurement_id, start_seconds,
               end_seconds):
    """
    Return data from start_seconds to end_seconds from influxdb.
    Used for asynchronous graph display of many points (up to millions).
    """
    if device_type == 'tag':
        notes_list = []
        tag = NoteTags.query.filter(NoteTags.unique_id == device_id).first()

        start = datetime.datetime.utcfromtimestamp(float(start_seconds))
        if end_seconds == '0':
            end = datetime.datetime.utcnow()
        else:
            end = datetime.datetime.utcfromtimestamp(float(end_seconds))

        notes = Notes.query.filter(
            and_(Notes.date_time >= start, Notes.date_time <= end)).all()
        for each_note in notes:
            if tag.unique_id in each_note.tags.split(','):
                notes_list.append([
                    each_note.date_time.strftime(
                        "%Y-%m-%dT%H:%M:%S.000000000Z"), each_note.name,
                    each_note.note
                ])

        if notes_list:
            return jsonify(notes_list)
        else:
            return '', 204

    dbcon = InfluxDBClient(INFLUXDB_HOST, INFLUXDB_PORT, INFLUXDB_USER,
                           INFLUXDB_PASSWORD, INFLUXDB_DATABASE)

    if device_type in ['input', 'math', 'output', 'pid']:
        measure = DeviceMeasurements.query.filter(
            DeviceMeasurements.unique_id == measurement_id).first()
    else:
        measure = None

    if not measure:
        return "Could not find measurement"

    if measure:
        conversion = Conversion.query.filter(
            Conversion.unique_id == measure.conversion_id).first()
    else:
        conversion = None
    channel, unit, measurement = return_measurement_info(measure, conversion)

    # Set the time frame to the past year if start/end not specified
    if start_seconds == '0' and end_seconds == '0':
        # Get how many points there are in the past year
        query_str = query_string(unit,
                                 device_id,
                                 measure=measurement,
                                 channel=channel,
                                 value='COUNT')

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        count_points = raw_data['series'][0]['values'][0][1]
        # Get the timestamp of the first point in the past year
        query_str = query_string(unit,
                                 device_id,
                                 measure=measurement,
                                 channel=channel,
                                 limit=1)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        first_point = raw_data['series'][0]['values'][0][0]
        end = datetime.datetime.utcnow()
        end_str = end.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
    # Set the time frame to the past start epoch to now
    elif start_seconds != '0' and end_seconds == '0':
        start = datetime.datetime.utcfromtimestamp(float(start_seconds))
        start_str = start.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
        end = datetime.datetime.utcnow()
        end_str = end.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

        query_str = query_string(unit,
                                 device_id,
                                 measure=measurement,
                                 channel=channel,
                                 value='COUNT',
                                 start_str=start_str,
                                 end_str=end_str)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        count_points = raw_data['series'][0]['values'][0][1]
        # Get the timestamp of the first point in the past year

        query_str = query_string(unit,
                                 device_id,
                                 measure=measurement,
                                 channel=channel,
                                 start_str=start_str,
                                 end_str=end_str,
                                 limit=1)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        first_point = raw_data['series'][0]['values'][0][0]
    else:
        start = datetime.datetime.utcfromtimestamp(float(start_seconds))
        start_str = start.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
        end = datetime.datetime.utcfromtimestamp(float(end_seconds))
        end_str = end.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

        query_str = query_string(unit,
                                 device_id,
                                 measure=measurement,
                                 channel=channel,
                                 value='COUNT',
                                 start_str=start_str,
                                 end_str=end_str)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        count_points = raw_data['series'][0]['values'][0][1]
        # Get the timestamp of the first point in the past year

        query_str = query_string(unit,
                                 device_id,
                                 measure=measurement,
                                 channel=channel,
                                 start_str=start_str,
                                 end_str=end_str,
                                 limit=1)

        if query_str == 1:
            return '', 204
        raw_data = dbcon.query(query_str).raw

        first_point = raw_data['series'][0]['values'][0][0]

    start = datetime.datetime.strptime(
        influx_time_str_to_milliseconds(first_point), '%Y-%m-%dT%H:%M:%S.%f')
    start_str = start.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

    logger.debug('Count = {}'.format(count_points))
    logger.debug('Start = {}'.format(start))
    logger.debug('End   = {}'.format(end))

    # How many seconds between the start and end period
    time_difference_seconds = (end - start).total_seconds()
    logger.debug('Difference seconds = {}'.format(time_difference_seconds))

    # If there are more than 700 points in the time frame, we need to group
    # data points into 700 groups with points averaged in each group.
    if count_points > 700:
        # Average period between input reads
        seconds_per_point = time_difference_seconds / count_points
        logger.debug('Seconds per point = {}'.format(seconds_per_point))

        # How many seconds to group data points in
        group_seconds = int(time_difference_seconds / 700)
        logger.debug('Group seconds = {}'.format(group_seconds))

        try:
            query_str = query_string(unit,
                                     device_id,
                                     measure=measurement,
                                     channel=channel,
                                     value='MEAN',
                                     start_str=start_str,
                                     end_str=end_str,
                                     group_sec=group_seconds)

            if query_str == 1:
                return '', 204
            raw_data = dbcon.query(query_str).raw

            return jsonify(raw_data['series'][0]['values'])
        except Exception as e:
            logger.error("URL for 'async_data' raised and error: "
                         "{err}".format(err=e))
            return '', 204
    else:
        try:
            query_str = query_string(unit,
                                     device_id,
                                     measure=measurement,
                                     channel=channel,
                                     start_str=start_str,
                                     end_str=end_str)

            if query_str == 1:
                return '', 204
            raw_data = dbcon.query(query_str).raw

            return jsonify(raw_data['series'][0]['values'])
        except Exception as e:
            logger.error("URL for 'async_data' raised and error: "
                         "{err}".format(err=e))
            return '', 204
Exemple #37
0
def export_data(unique_id, measurement_id, start_seconds, end_seconds):
    """
    Return data from start_seconds to end_seconds from influxdb.
    Used for exporting data.
    """
    dbcon = InfluxDBClient(INFLUXDB_HOST,
                           INFLUXDB_PORT,
                           INFLUXDB_USER,
                           INFLUXDB_PASSWORD,
                           INFLUXDB_DATABASE,
                           timeout=100)

    output = Output.query.filter(Output.unique_id == unique_id).first()
    input_dev = Input.query.filter(Input.unique_id == unique_id).first()
    math = Math.query.filter(Math.unique_id == unique_id).first()

    if output:
        name = output.name
    elif input_dev:
        name = input_dev.name
    elif math:
        name = math.name
    else:
        name = None

    device_measurement = DeviceMeasurements.query.filter(
        DeviceMeasurements.unique_id == measurement_id).first()
    if device_measurement:
        conversion = Conversion.query.filter(
            Conversion.unique_id == device_measurement.conversion_id).first()
    else:
        conversion = None
    channel, unit, measurement = return_measurement_info(
        device_measurement, conversion)

    utc_offset_timedelta = datetime.datetime.utcnow() - datetime.datetime.now()
    start = datetime.datetime.fromtimestamp(float(start_seconds))
    start += utc_offset_timedelta
    start_str = start.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
    end = datetime.datetime.fromtimestamp(float(end_seconds))
    end += utc_offset_timedelta
    end_str = end.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

    query_str = query_string(unit,
                             unique_id,
                             measure=measurement,
                             channel=channel,
                             start_str=start_str,
                             end_str=end_str)
    if query_str == 1:
        flash('Invalid query string', 'error')
        return redirect(url_for('routes_page.page_export'))
    raw_data = dbcon.query(query_str).raw

    if not raw_data or 'series' not in raw_data or not raw_data['series']:
        flash('No measurements to export in this time period', 'error')
        return redirect(url_for('routes_page.page_export'))

    # Generate column names
    col_1 = 'timestamp (UTC)'
    col_2 = '{name} {meas} ({id})'.format(name=name,
                                          meas=measurement,
                                          id=unique_id)
    csv_filename = '{id}_{name}_{meas}.csv'.format(id=unique_id,
                                                   name=name,
                                                   meas=measurement)

    from flask import Response
    import csv
    from io import StringIO

    def iter_csv(data):
        """ Stream CSV file to user for download """
        line = StringIO()
        writer = csv.writer(line)
        writer.writerow([col_1, col_2])
        for csv_line in data:
            writer.writerow(
                [str(csv_line[0][:-4]).replace('T', ' '), csv_line[1]])
            line.seek(0)
            yield line.read()
            line.truncate(0)
            line.seek(0)

    response = Response(iter_csv(raw_data['series'][0]['values']),
                        mimetype='text/csv')
    response.headers[
        'Content-Disposition'] = 'attachment; filename="{}"'.format(
            csv_filename)
    return response
Exemple #38
0
def last_data_pid(pid_id, input_period):
    """Return the most recent time and value from influxdb"""
    if not str_is_float(input_period):
        return '', 204

    try:
        pid = PID.query.filter(PID.unique_id == pid_id).first()
        device_id = pid.measurement.split(',')[0]
        measurement_id = pid.measurement.split(',')[1]

        actual_measurement = DeviceMeasurements.query.filter(
            DeviceMeasurements.unique_id == measurement_id).first()
        if actual_measurement:
            actual_cnversion = Conversion.query.filter(
                Conversion.unique_id == actual_measurement.conversion_id).first()
        else:
            actual_cnversion = None

        (actual_channel,
         actual_unit,
         actual_measurement) = return_measurement_info(
            actual_measurement, actual_cnversion)

        setpoint_measurement = None
        setpoint_unit = None
        setpoint_pid = PID.query.filter(PID.unique_id == pid_id).first()
        if setpoint_pid and ',' in setpoint_pid.measurement:
            pid_measurement = setpoint_pid.measurement.split(',')[1]
            setpoint_measurement = DeviceMeasurements.query.filter(
                DeviceMeasurements.unique_id == pid_measurement).first()
            if setpoint_measurement:
                conversion = Conversion.query.filter(
                    Conversion.unique_id == setpoint_measurement.conversion_id).first()
                _, setpoint_unit, _ = return_measurement_info(setpoint_measurement, conversion)

        p_value = return_point_timestamp(
            pid_id, 'pid_value', input_period, measurement='pid_p_value')
        i_value = return_point_timestamp(
            pid_id, 'pid_value', input_period, measurement='pid_i_value')
        d_value = return_point_timestamp(
            pid_id, 'pid_value', input_period, measurement='pid_d_value')
        pid_value = [p_value[0], '{:.3f}'.format(float(p_value[1]) + float(i_value[1]) + float(d_value[1]))]

        live_data = {
            'activated': pid.is_activated,
            'paused': pid.is_paused,
            'held': pid.is_held,
            'setpoint': return_point_timestamp(
                pid_id, setpoint_unit, input_period, measurement=setpoint_measurement.measurement),
            'pid_p_value': p_value,
            'pid_i_value': i_value,
            'pid_d_value': d_value,
            'pid_pid_value': pid_value,
            'duration_time': return_point_timestamp(
                pid_id, 's', input_period, measurement='duration_time'),
            'duty_cycle': return_point_timestamp(
                pid_id, 'percent', input_period, measurement='duty_cycle'),
            'actual': return_point_timestamp(
                device_id,
                actual_unit,
                input_period,
                measurement=actual_measurement,
                channel=actual_channel)
        }
        return jsonify(live_data)
    except KeyError:
        logger.debug("No Data returned form influxdb")
        return '', 204
    except Exception as e:
        logger.exception("URL for 'last_pid' raised and error: "
                         "{err}".format(err=e))
        return '', 204
    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)

        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)

        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."
            )
Exemple #40
0
    def setup_output(self):
        import pigpio

        self.pigpio = pigpio

        self.setup_output_variables(OUTPUT_INFORMATION)

        error = []
        if self.options_channels['pin'][0] is None:
            error.append("Pin must be set")
        if self.options_channels['pwm_hertz'][0] <= 0:
            error.append("PWM Hertz must be a positive value")
        if error:
            for each_error in error:
                self.logger.error(each_error)
            return

        try:
            self.pwm_output = self.pigpio.pi()
            if not self.pwm_output.connected:
                self.logger.error("Could not connect to pigpiod")
                self.pwm_output = None
                return
            if self.options_channels['pwm_library'][0] == 'pigpio_hardware':
                self.pwm_output.hardware_PWM(
                    self.options_channels['pin'][0],
                    self.options_channels['pwm_hertz'][0], 0)
                self.logger.info(
                    "Output setup on Hardware pin {pin} at {hz} Hertz".format(
                        pin=self.options_channels['pin'][0],
                        hz=self.options_channels['pwm_hertz'][0]))
            elif self.options_channels['pwm_library'][0] == 'pigpio_any':
                self.pwm_output.set_PWM_frequency(
                    self.options_channels['pin'][0],
                    self.options_channels['pwm_hertz'][0])
                self.logger.info(
                    "Output setup on Any pin {pin} at {hz} Hertz".format(
                        pin=self.options_channels['pin'][0],
                        hz=self.options_channels['pwm_hertz'][0]))

            self.output_setup = True

            state_string = ""
            if self.options_channels['state_startup'][0] == 0:
                self.output_switch('off')
                self.logger.info("PWM turned off (0 % duty cycle)")
            elif self.options_channels['state_startup'][0] == 'set_duty_cycle':
                self.output_switch(
                    'on', amount=self.options_channels['startup_value'][0])
                self.logger.info(
                    "PWM set to {} % duty cycle (user-specified value)".format(
                        self.options_channels['startup_value'][0]))
            elif self.options_channels['state_startup'][
                    0] == 'last_duty_cycle':
                device_measurement = db_retrieve_table_daemon(
                    DeviceMeasurements).filter(
                        and_(DeviceMeasurements.device_id == self.unique_id,
                             DeviceMeasurements.channel == 0)).first()

                last_measurement = None
                if device_measurement:
                    channel, unit, measurement = return_measurement_info(
                        device_measurement, None)
                    last_measurement = read_last_influxdb(self.unique_id,
                                                          unit,
                                                          channel,
                                                          measure=measurement,
                                                          duration_sec=None)

                if last_measurement:
                    self.logger.info(
                        "PWM set to {} % duty cycle (last known value)".format(
                            last_measurement[1]))
                    self.output_switch('on', amount=last_measurement[1])
                else:
                    self.logger.error(
                        "Output instructed at startup to be set to "
                        "the last known duty cycle, but a last known "
                        "duty cycle could not be found in the measurement "
                        "database")
        except Exception as except_msg:
            self.logger.exception(
                "Output was unable to be setup on pin {pin}: {err}".format(
                    pin=self.options_channels['pin'][0], err=except_msg))
Exemple #41
0
    def math_humidity(self, measurement_dict):
        pressure_pa = 101325
        critical_error = False

        if self.pressure_pa_id and self.pressure_pa_measure_id:

            success_pa, pressure = self.get_measurements_from_id(
                self.pressure_pa_id, self.pressure_pa_measure_id)

            if success_pa:
                pressure_pa = int(pressure[1])
                # Pressure must be in Pa, convert if not

                measurement_press = db_retrieve_table_daemon(
                    DeviceMeasurements, unique_id=self.pressure_pa_measure_id)
                if not measurement_press:
                    self.logger.error("Could not find pressure measurement")
                    measurement_press = None
                    critical_error = True

                if measurement_press and measurement_press.unit != 'Pa':
                    pressure_pa, status = self.is_measurement_unit(
                        measurement_press.unit, 'Pa', pressure_pa)
                    if status == 'error':
                        critical_error = True

        success_dbt, dry_bulb_t = self.get_measurements_from_id(
            self.dry_bulb_t_id, self.dry_bulb_t_measure_id)
        success_wbt, wet_bulb_t = self.get_measurements_from_id(
            self.wet_bulb_t_id, self.wet_bulb_t_measure_id)

        if success_dbt and success_wbt:
            dbt_kelvin = float(dry_bulb_t[1])
            wbt_kelvin = float(wet_bulb_t[1])

            measurement_db_temp = db_retrieve_table_daemon(
                DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id)
            if not measurement_db_temp:
                self.logger.error(
                    "Could not find dry bulb temperature measurement")
                measurement_db_temp = None
                critical_error = True

            if measurement_db_temp and measurement_db_temp.unit != 'K':
                dbt_kelvin, status = self.is_measurement_unit(
                    measurement_db_temp.unit, 'K', dbt_kelvin)
                if status == 'error':
                    critical_error = True

            measurement_wb_temp = db_retrieve_table_daemon(
                DeviceMeasurements, unique_id=self.wet_bulb_t_measure_id)
            if not measurement_wb_temp:
                self.logger.error(
                    "Could not find wet bulb temperature measurement")
                measurement_wb_temp = None
                critical_error = True

            if measurement_wb_temp and measurement_wb_temp.unit != 'K':
                wbt_kelvin, status = self.is_measurement_unit(
                    measurement_wb_temp.unit, 'K', wbt_kelvin)
                if status == 'error':
                    critical_error = True

            # Convert temperatures to Kelvin (already done above)
            # dbt_kelvin = celsius_to_kelvin(dry_bulb_t_c)
            # wbt_kelvin = celsius_to_kelvin(wet_bulb_t_c)
            psypi = None

            try:
                if not critical_error:
                    psypi = SI.state("DBT", dbt_kelvin, "WBT", wbt_kelvin,
                                     pressure_pa)
                else:
                    self.logger.error(
                        "One or more critical errors prevented the "
                        "humidity from being calculated")
            except TypeError as err:
                self.logger.error("TypeError: {msg}".format(msg=err))

            if psypi:
                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])

                list_measurement = [
                    specific_enthalpy, humidity, specific_volume,
                    humidity_ratio
                ]

                for each_measurement in self.device_measurements.all():
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=each_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        each_measurement, conversion)

                    measurement_dict[channel] = {
                        'measurement': measurement,
                        'unit': unit,
                        'value': list_measurement[channel]
                    }
        else:
            self.error_not_within_max_age()
        return measurement_dict
Exemple #42
0
    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))
                    return False
                else:
                    measure.append(last_measurement[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_measurement in self.device_measurements.all():
                if each_measurement.is_enabled:
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=each_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        each_measurement, conversion)

                    write_influxdb_value(self.unique_id,
                                         unit,
                                         value=list_measurement[channel],
                                         measure=measurement,
                                         channel=0)
Exemple #43
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.log_level_debug = form_mod_math.log_level_debug.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]

                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]

                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_input'))
Exemple #44
0
def last_data(unique_id, measure_type, measurement_id, period):
    """Return the most recent time and value from influxdb"""
    if not str_is_float(period):
        return '', 204

    if measure_type in ['input', 'math', 'output', 'pid']:
        current_app.config['INFLUXDB_USER'] = INFLUXDB_USER
        current_app.config['INFLUXDB_PASSWORD'] = INFLUXDB_PASSWORD
        current_app.config['INFLUXDB_DATABASE'] = INFLUXDB_DATABASE
        current_app.config['INFLUXDB_TIMEOUT'] = 5
        dbcon = influx_db.connection

        if measure_type in ['input', 'math', 'pid']:
            measure = DeviceMeasurements.query.filter(
                DeviceMeasurements.unique_id == measurement_id).first()
        elif measure_type == 'output':
            measure = Output.query.filter(
                Output.unique_id == unique_id).first()
        else:
            return '', 204

        if measure:
            conversion = Conversion.query.filter(
                Conversion.unique_id == measure.conversion_id).first()
        else:
            conversion = None

        channel, unit, measurement = return_measurement_info(
            measure, conversion)

        if hasattr(measure, 'measurement_type') and measure.measurement_type == 'setpoint':
            setpoint_pid = PID.query.filter(PID.unique_id == measure.device_id).first()
            if setpoint_pid and ',' in setpoint_pid.measurement:
                pid_measurement = setpoint_pid.measurement.split(',')[1]
                setpoint_measurement = DeviceMeasurements.query.filter(
                    DeviceMeasurements.unique_id == pid_measurement).first()
                if setpoint_measurement:
                    conversion = Conversion.query.filter(
                        Conversion.unique_id == setpoint_measurement.conversion_id).first()
                    _, unit, measurement = return_measurement_info(setpoint_measurement, conversion)

        try:
            if period != '0':
                query_str = query_string(
                    unit, unique_id,
                    measure=measurement, channel=channel,
                    value='LAST', past_sec=period)
            else:
                query_str = query_string(
                    unit, unique_id,
                    measure=measurement, channel=channel,
                    value='LAST')
            if query_str == 1:
                return '', 204
            raw_data = dbcon.query(query_str).raw
            number = len(raw_data['series'][0]['values'])
            time_raw = raw_data['series'][0]['values'][number - 1][0]
            value = raw_data['series'][0]['values'][number - 1][1]
            value = float(value)
            # Convert date-time to epoch (potential bottleneck for data)
            dt = date_parse(time_raw)
            timestamp = calendar.timegm(dt.timetuple()) * 1000
            live_data = '[{},{}]'.format(timestamp, value)
            return Response(live_data, mimetype='text/json')
        except KeyError:
            logger.debug("No Data returned form influxdb")
            return '', 204
        except Exception as e:
            logger.exception("URL for 'last_data' raised and error: "
                             "{err}".format(err=e))
            return '', 204