Beispiel #1
0
    def __init__(self):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger("mycodo.output")

        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.control = DaemonControl()

        self.output_id = {}
        self.output_unique_id = {}
        self.output_type = {}
        self.output_name = {}
        self.output_pin = {}
        self.output_amps = {}
        self.output_trigger = {}
        self.output_on_at_start = {}
        self.output_on_until = {}
        self.output_last_duration = {}
        self.output_on_duration = {}

        # wireless
        self.output_protocol = {}
        self.output_pulse_length = {}
        self.output_bit_length = {}
        self.output_on_command = {}
        self.output_off_command = {}
        self.wireless_pi_switch = {}

        # PWM
        self.pwm_hertz = {}
        self.pwm_library = {}
        self.pwm_output = {}
        self.pwm_state = {}
        self.pwm_time_turned_on = {}

        self.output_time_turned_on = {}

        self.logger.debug("Initializing Outputs")
        try:
            smtp = db_retrieve_table_daemon(SMTP, entry='first')
            self.smtp_max_count = smtp.hourly_max
            self.smtp_wait_time = time.time() + 3600
            self.smtp_timer = time.time()
            self.email_count = 0
            self.allowed_to_send_notice = True

            outputs = db_retrieve_table_daemon(Output, entry='all')
            self.all_outputs_initialize(outputs)
            # Turn all outputs off
            self.all_outputs_off()
            # Turn outputs on that are set to be on at start
            self.all_outputs_on()
            self.logger.debug("Outputs Initialized")

        except Exception as except_msg:
            self.logger.exception(
                "Problem initializing outputs: {err}".format(err=except_msg))

        self.running = False
Beispiel #2
0
    def initialize_values(self):
        """Set PID parameters"""
        pid = db_retrieve_table_daemon(PID, device_id=self.pid_id)
        self.is_activated = pid.is_activated
        self.is_held = pid.is_held
        self.is_paused = pid.is_paused
        self.measurement = pid.measurement
        self.method_id = pid.method_id
        self.direction = pid.direction
        self.raise_relay_id = pid.raise_relay_id
        self.raise_min_duration = pid.raise_min_duration
        self.raise_max_duration = pid.raise_max_duration
        self.raise_min_off_duration = pid.raise_min_off_duration
        self.lower_relay_id = pid.lower_relay_id
        self.lower_min_duration = pid.lower_min_duration
        self.lower_max_duration = pid.lower_max_duration
        self.lower_min_off_duration = pid.lower_min_off_duration
        self.Kp = pid.p
        self.Ki = pid.i
        self.Kd = pid.d
        self.integrator_min = pid.integrator_min
        self.integrator_max = pid.integrator_max
        self.measure_interval = pid.period
        self.max_measure_age = pid.max_measure_age
        self.default_set_point = pid.setpoint
        self.set_point = pid.setpoint

        sensor = db_retrieve_table_daemon(Sensor, device_id=pid.sensor_id)
        self.sensor_unique_id = sensor.unique_id
        self.sensor_duration = sensor.period

        return "success"
Beispiel #3
0
    def __init__(self, ready, pid_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger("mycodo.pid_{id}".format(id=pid_id))

        self.running = False
        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.pid_id = pid_id
        self.pid_unique_id = db_retrieve_table_daemon(
            PID, device_id=self.pid_id).unique_id
        self.control = DaemonControl()

        self.control_variable = 0
        self.derivator = 0
        self.integrator = 0
        self.error = 0.0
        self.P_value = None
        self.I_value = None
        self.D_value = None
        self.set_point = 0
        self.lower_seconds_on = 0
        self.raise_seconds_on = 0
        self.last_measurement_success = False

        self.initialize_values()

        self.timer = t.time() + self.measure_interval

        # Check if a method is set for this PID
        if self.method_id:
            method = db_retrieve_table_daemon(Method, device_id=self.method_id)
            self.method_type = method.method_type
            self.method_start_time = method.start_time

            if self.method_type == 'Duration':
                if self.method_start_time == 'Ended':
                    # Method has ended and hasn't been instructed to begin again
                    pass
                elif self.method_start_time == 'Ready' or self.method_start_time is None:
                    # Method has been instructed to begin
                    with session_scope(MYCODO_DB_PATH) as db_session:
                        mod_method = db_session.query(Method)
                        mod_method = mod_method.filter(
                            Method.id == self.method_id).first()
                        mod_method.time_start = datetime.datetime.now()
                        self.method_start_time = mod_method.start_time
                        db_session.commit()
                else:
                    # Method neither instructed to begin or not to
                    # Likely there was a daemon restart ot power failure
                    # Resume method with saved start_time
                    self.method_start_time = datetime.datetime.strptime(
                        str(self.method_start_time), '%Y-%m-%d %H:%M:%S.%f')
                    self.logger.warning(
                        "Resuming method {id} started at {time}".format(
                            id=self.method_id, time=self.method_start_time))
Beispiel #4
0
 def relay_state(relay_id):
     relay = db_retrieve_table_daemon(Relay, unique_id=relay_id)
     GPIO.setmode(GPIO.BCM)
     if GPIO.input(relay.pin) == relay.trigger:
         gpio_state = 'On'
     else:
         gpio_state = 'Off'
     return gpio_state
Beispiel #5
0
 def output_state(output_id):
     output = db_retrieve_table_daemon(Output, unique_id=output_id)
     GPIO.setmode(GPIO.BCM)
     if GPIO.input(output.pin) == output.trigger:
         gpio_state = 'On'
     else:
         gpio_state = 'Off'
     return gpio_state
Beispiel #6
0
    def add_mod_output(self, output_id):
        """
        Add or modify local dictionary of output settings form SQL database

        When a output is added or modified while the output controller is
        running, these local variables need to also be modified to
        maintain consistency between the SQL database and running controller.

        :param output_id: Unique ID for each output
        :type output_id: int

        :return: 0 for success, 1 for fail, with success for fail message
        :rtype: int, str
        """
        output_id = int(output_id)
        try:
            output = db_retrieve_table_daemon(Output, device_id=output_id)

            self.output_type[output_id] = output.relay_type

            # Turn current pin off
            if output_id in self.output_pin and self.output_state(output_id) != 'off':
                self.output_switch(output_id, 'off')

            self.output_id[output_id] = output.id
            self.output_unique_id[output_id] = output.unique_id
            self.output_type[output_id] = output.relay_type
            self.output_name[output_id] = output.name
            self.output_pin[output_id] = output.pin
            self.output_amps[output_id] = output.amps
            self.output_trigger[output_id] = output.trigger
            self.output_on_at_start[output_id] = output.on_at_start
            self.output_on_until[output_id] = datetime.datetime.now()
            self.output_time_turned_on[output_id] = None
            self.output_last_duration[output_id] = 0
            self.output_on_duration[output_id] = False
            self.output_protocol[output_id] = output.protocol
            self.output_pulse_length[output_id] = output.pulse_length
            self.output_bit_length[output_id] = output.bit_length
            self.output_on_command[output_id] = output.on_command
            self.output_off_command[output_id] = output.off_command

            self.pwm_hertz[output_id] = output.pwm_hertz
            self.pwm_library[output_id] = output.pwm_library

            if self.output_pin[output_id]:
                self.setup_pin(output.id)

            message = u"Output {id} ({name}) initialized".format(
                id=self.output_id[output_id],
                name=self.output_name[output_id])
            self.logger.debug(message)

            return 0, "success"
        except Exception as except_msg:
            self.logger.exception(1)
            return 1, "Add_Mod_Output Error: ID {id}: {err}".format(
                id=output_id, err=except_msg)
Beispiel #7
0
    def initialize_values(self):
        """Set PID parameters"""
        pid = db_retrieve_table_daemon(PID, device_id=self.pid_id)
        self.is_activated = pid.is_activated
        self.is_held = pid.is_held
        self.is_paused = pid.is_paused
        self.method_id = pid.method_id
        self.direction = pid.direction
        self.raise_output_id = pid.raise_relay_id
        self.raise_min_duration = pid.raise_min_duration
        self.raise_max_duration = pid.raise_max_duration
        self.raise_min_off_duration = pid.raise_min_off_duration
        self.lower_output_id = pid.lower_relay_id
        self.lower_min_duration = pid.lower_min_duration
        self.lower_max_duration = pid.lower_max_duration
        self.lower_min_off_duration = pid.lower_min_off_duration
        self.Kp = pid.p
        self.Ki = pid.i
        self.Kd = pid.d
        self.integrator_min = pid.integrator_min
        self.integrator_max = pid.integrator_max
        self.period = pid.period
        self.max_measure_age = pid.max_measure_age
        self.default_set_point = pid.setpoint
        self.set_point = pid.setpoint

        input_unique_id = pid.measurement.split(',')[0]
        self.measurement = pid.measurement.split(',')[1]

        input_dev = db_retrieve_table_daemon(Input, unique_id=input_unique_id)
        self.input_unique_id = input_dev.unique_id
        self.input_duration = input_dev.period

        try:
            self.raise_output_type = db_retrieve_table_daemon(
                Output, device_id=self.raise_output_id).relay_type
        except AttributeError:
            self.raise_output_type = None
        try:
            self.lower_output_type = db_retrieve_table_daemon(
                Output, device_id=self.lower_output_id).relay_type
        except AttributeError:
            self.lower_output_type = None

        return "success"
Beispiel #8
0
    def __init__(self):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger("mycodo.relay")

        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.control = DaemonControl()

        self.relay_id = {}
        self.relay_unique_id = {}
        self.relay_name = {}
        self.relay_pin = {}
        self.relay_amps = {}
        self.relay_trigger = {}
        self.relay_on_at_start = {}
        self.relay_on_until = {}
        self.relay_last_duration = {}
        self.relay_on_duration = {}

        self.relay_time_turned_on = {}

        self.logger.debug("Initializing Relays")
        try:

            smtp = db_retrieve_table_daemon(SMTP, entry='first')
            self.smtp_max_count = smtp.hourly_max
            self.smtp_wait_time = time.time() + 3600
            self.smtp_timer = time.time()
            self.email_count = 0
            self.allowed_to_send_notice = True

            relays = db_retrieve_table_daemon(Relay, entry='all')
            self.all_relays_initialize(relays)
            # Turn all relays off
            self.all_relays_off()
            # Turn relays on that are set to be on at start
            self.all_relays_on()
            self.logger.debug("Relays Initialized")

        except Exception as except_msg:
            self.logger.exception(
                "Problem initializing relays: {err}", err=except_msg)

        self.running = False
Beispiel #9
0
    def __init__(self, ready, timer_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger(
            "mycodo.timer_{id}".format(id=timer_id))

        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.timer_id = timer_id
        self.control = DaemonControl()

        timer = db_retrieve_table_daemon(Timer, device_id=self.timer_id)
        self.timer_type = timer.timer_type
        self.name = timer.name
        self.relay_unique_id = timer.relay_id
        self.state = timer.state
        self.time_start = timer.time_start
        self.time_end = timer.time_end
        self.duration_on = timer.duration_on
        self.duration_off = timer.duration_off

        self.relay_id = db_retrieve_table_daemon(
            Relay, unique_id=self.relay_unique_id).id

        # Time of day split into hour and minute
        if self.time_start:
            time_split = self.time_start.split(":")
            self.start_hour = time_split[0]
            self.start_minute = time_split[1]
        else:
            self.start_hour = None
            self.start_minute = None

        if self.time_end:
            time_split = self.time_end.split(":")
            self.end_hour = time_split[0]
            self.end_minute = time_split[1]
        else:
            self.end_hour = None
            self.end_minute = None

        self.duration_timer = time.time()
        self.date_timer_not_executed = True
        self.running = False
Beispiel #10
0
    def setup_sensor_conditionals(self, cond_mod='setup'):
        # Signal to pause the main loop and wait for verification
        self.pause_loop = True
        while not self.verify_pause_loop:
            time.sleep(0.1)

        self.cond_id = {}
        self.cond_action_id = {}
        self.cond_name = {}
        self.cond_is_activated = {}
        self.cond_if_sensor_period = {}
        self.cond_if_sensor_measurement = {}
        self.cond_if_sensor_edge_select = {}
        self.cond_if_sensor_edge_detected = {}
        self.cond_if_sensor_gpio_state = {}
        self.cond_if_sensor_direction = {}
        self.cond_if_sensor_setpoint = {}

        sensor_conditional = db_retrieve_table_daemon(
            Conditional)
        sensor_conditional = sensor_conditional.filter(
            Conditional.sensor_id == self.sensor_id)
        sensor_conditional = sensor_conditional.filter(
            Conditional.is_activated == True).all()

        if cond_mod == 'setup':
            self.cond_timer = {}
            self.smtp_wait_timer = {}
        elif cond_mod == 'add':
            self.logger.debug("Added Conditional")
        elif cond_mod == 'del':
            self.logger.debug("Deleted Conditional")
        elif cond_mod == 'mod':
            self.logger.debug("Modified Conditional")
        else:
            return 1

        for each_cond in sensor_conditional:
            if cond_mod == 'setup':
                self.logger.info("Activated Conditional ({id})".format(id=each_cond.id))
            self.cond_id[each_cond.id] = each_cond.id
            self.cond_is_activated[each_cond.id] = each_cond.is_activated
            self.cond_if_sensor_period[each_cond.id] = each_cond.if_sensor_period
            self.cond_if_sensor_measurement[each_cond.id] = each_cond.if_sensor_measurement
            self.cond_if_sensor_edge_select[each_cond.id] = each_cond.if_sensor_edge_select
            self.cond_if_sensor_edge_detected[each_cond.id] = each_cond.if_sensor_edge_detected
            self.cond_if_sensor_gpio_state[each_cond.id] = each_cond.if_sensor_gpio_state
            self.cond_if_sensor_direction[each_cond.id] = each_cond.if_sensor_direction
            self.cond_if_sensor_setpoint[each_cond.id] = each_cond.if_sensor_setpoint
            self.cond_timer[each_cond.id] = time.time() + each_cond.if_sensor_period
            self.smtp_wait_timer[each_cond.id] = time.time() + 3600

        self.pause_loop = False
        self.verify_pause_loop = False
Beispiel #11
0
    def create_lcd_line(self, last_measurement_success, i):
        try:
            if last_measurement_success:
                # Determine if the LCD output will have a value unit
                measurement = ''
                if self.lcd_line[i]['measurement'] == 'setpoint':
                    pid = db_retrieve_table_daemon(
                        PID, unique_id=self.lcd_line[i]['id'])
                    measurement = pid.measurement
                    self.lcd_line[i]['measurement_value'] = '{:.2f}'.format(
                        self.lcd_line[i]['measurement_value'])
                elif self.lcd_line[i]['measurement'] == 'duration_sec':
                    measurement = 'duration_sec'
                    self.lcd_line[i]['measurement_value'] = '{:.2f}'.format(
                        self.lcd_line[i]['measurement_value'])
                elif self.lcd_line[i]['measurement'] in MEASUREMENT_UNITS:
                    measurement = self.lcd_line[i]['measurement']

                # Produce the line that will be displayed on the LCD
                number_characters = self.lcd_x_characters
                if self.lcd_line[i]['measurement'] == 'time':
                    # Convert UTC timestamp to local timezone
                    utc_dt = datetime.datetime.strptime(
                        self.lcd_line[i]['time'].split(".")[0],
                        '%Y-%m-%dT%H:%M:%S')
                    utc_timestamp = calendar.timegm(utc_dt.timetuple())
                    self.lcd_string_line[i] = str(
                        datetime.datetime.fromtimestamp(utc_timestamp))
                elif measurement:
                    value_length = len(
                        str(self.lcd_line[i]['measurement_value']))
                    unit_length = len(MEASUREMENT_UNITS[measurement]['unit'])
                    name_length = number_characters - value_length - unit_length - 2
                    name_cropped = self.lcd_line[i]['name'].ljust(
                        name_length)[:name_length]
                    self.lcd_string_line[i] = u'{name} {value} {unit}'.format(
                        name=name_cropped,
                        value=self.lcd_line[i]['measurement_value'],
                        unit=MEASUREMENT_UNITS[measurement]['unit'])
                else:
                    value_length = len(
                        str(self.lcd_line[i]['measurement_value']))
                    name_length = number_characters - value_length - 1
                    name_cropped = self.lcd_line[i]['name'][:name_length]
                    self.lcd_string_line[i] = u'{name} {value}'.format(
                        name=name_cropped,
                        value=self.lcd_line[i]['measurement_value'])
            else:
                self.lcd_string_line[i] = 'ERROR: NO DATA'
        except Exception as except_msg:
            self.logger.exception("Error: {err}".format(err=except_msg))
Beispiel #12
0
 def setup_lcd_line(self, line, device_id, measurement):
     self.lcd_line[line]['id'] = device_id
     self.lcd_line[line]['measurement'] = measurement
     if device_id:
         table = None
         if measurement in self.list_relays:
             table = Relay
         elif measurement in self.list_pids:
             table = PID
         elif measurement in self.list_sensors:
             table = Sensor
         sensor_line = db_retrieve_table_daemon(table, unique_id=device_id)
         self.lcd_line[line]['name'] = sensor_line.name
         if 'time' in measurement:
             self.lcd_line[line]['measurement'] = 'time'
Beispiel #13
0
 def setup_lcd_line(self, display_id, line, lcd_id, measurement):
     self.lcd_line[display_id][line]['id'] = lcd_id
     self.lcd_line[display_id][line]['measurement'] = measurement
     if lcd_id:
         table = None
         if measurement in self.list_outputs:
             table = Output
         elif measurement in self.list_pids:
             table = PID
         elif measurement in self.list_inputs:
             table = Input
         input_line = db_retrieve_table_daemon(table, unique_id=lcd_id)
         self.lcd_line[display_id][line]['name'] = input_line.name
         if 'time' in measurement:
             self.lcd_line[display_id][line]['measurement'] = 'time'
Beispiel #14
0
    def add_mod_relay(self, relay_id, do_setup_pin=False):
        """
        Add or modify local dictionary of relay settings form SQL database

        When a relay is added or modified while the relay controller is
        running, these local variables need to also be modified to
        maintain consistency between the SQL database and running controller.

        :return: 0 for success, 1 for fail, with success for fail message
        :rtype: int, str

        :param relay_id: Unique ID for each relay
        :type relay_id: str
        :param do_setup_pin: If True, initialize GPIO (when adding new relay)
        :type do_setup_pin: bool
        """
        relay_id = int(relay_id)
        try:
            relay = db_retrieve_table_daemon(Relay, device_id=relay_id)
            self.relay_id[relay_id] = relay.id
            self.relay_unique_id[relay_id] = relay.unique_id
            self.relay_name[relay_id] = relay.name
            self.relay_pin[relay_id] = relay.pin
            self.relay_amps[relay_id] = relay.amps
            self.relay_trigger[relay_id] = relay.trigger
            self.relay_on_at_start[relay_id] = relay.on_at_start
            self.relay_on_until[relay_id] = datetime.datetime.now()
            self.relay_time_turned_on[relay_id] = None
            self.relay_last_duration[relay_id] = 0
            self.relay_on_duration[relay_id] = False
            message = "Relay {id} ({name}) ".format(
                id=self.relay_id[relay_id], name=self.relay_name[relay_id])
            if do_setup_pin and relay.pin:
                self.setup_pin(relay.id, relay.pin, relay.trigger)
                message += "initialized"
            else:
                message += "added"
            self.logger.debug(message)
            return 0, "success"
        except Exception as except_msg:
            return 1, "Add_Mod_Relay Error: ID {id}: {err}".format(
                id=relay_id, err=except_msg)
Beispiel #15
0
    def __init__(self, ready, math_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger("mycodo.math_{id}".format(id=math_id))

        self.running = False
        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.math_id = math_id

        math = db_retrieve_table_daemon(Math, device_id=self.math_id)
        self.math_unique_id = math.unique_id
        self.name = math.name
        self.math_type = math.math_type
        self.is_activated = math.is_activated
        self.period = math.period
        self.inputs = math.inputs
        self.max_measure_age = math.max_measure_age
        self.measure = math.measure
        self.measure_units = math.measure_units

        self.timer = t.time() + self.period
Beispiel #16
0
    def __init__(self, ready, lcd_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger("mycodo.lcd_{id}".format(id=lcd_id))

        self.running = False
        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.flash_lcd_on = False
        self.lcd_is_on = False
        self.lcd_id = lcd_id

        try:
            lcd = db_retrieve_table_daemon(LCD, device_id=self.lcd_id)

            self.lcd_name = lcd.name
            self.lcd_location = lcd.location
            self.lcd_period = lcd.period
            self.lcd_x_characters = lcd.x_characters
            self.lcd_y_lines = lcd.y_lines
            self.timer = time.time() + self.lcd_period
            self.backlight_timer = time.time()

            if lcd.multiplexer_address:
                self.multiplexer_address_string = lcd.multiplexer_address
                self.multiplexer_address = int(str(lcd.multiplexer_address),
                                               16)
                self.multiplexer_channel = lcd.multiplexer_channel
                self.multiplexer = TCA9548A(self.multiplexer_address)
            else:
                self.multiplexer = None

            self.lcd_line = {}
            for i in range(1, 5):
                self.lcd_line[i] = {}

            self.list_sensors = MEASUREMENT_UNITS
            self.list_sensors.update(
                {'sensor_time': {
                    'unit': None,
                    'name': 'Time'
                }})
            self.list_pids = ['setpoint', 'pid_time']
            self.list_relays = ['duration_sec', 'relay_time', 'relay_state']

            if self.lcd_y_lines in [2, 4]:
                self.setup_lcd_line(1, lcd.line_1_sensor_id,
                                    lcd.line_1_measurement)
                self.setup_lcd_line(2, lcd.line_2_sensor_id,
                                    lcd.line_2_measurement)

            if self.lcd_y_lines == 4:
                self.setup_lcd_line(3, lcd.line_3_sensor_id,
                                    lcd.line_3_measurement)
                self.setup_lcd_line(4, lcd.line_4_sensor_id,
                                    lcd.line_4_measurement)

            self.lcd_string_line = {}
            for i in range(1, self.lcd_y_lines + 1):
                self.lcd_string_line[i] = ''

            self.LCD_WIDTH = self.lcd_x_characters  # Max characters per line

            self.LCD_LINE = {1: 0x80, 2: 0xC0, 3: 0x94, 4: 0xD4}

            self.LCD_CHR = 1  # Mode - Sending data
            self.LCD_CMD = 0  # Mode - SenLCDding command

            self.LCD_BACKLIGHT = 0x08  # On
            self.LCD_BACKLIGHT_OFF = 0x00  # Off

            self.ENABLE = 0b00000100  # Enable bit

            # Timing constants
            self.E_PULSE = 0.0005
            self.E_DELAY = 0.0005

            # Setup I2C bus
            try:
                if GPIO.RPI_REVISION == 2 or GPIO.RPI_REVISION == 3:
                    i2c_bus_number = 1
                else:
                    i2c_bus_number = 0
                self.bus = smbus.SMBus(i2c_bus_number)
            except Exception as except_msg:
                self.logger.exception(
                    "Could not initialize I2C bus: {err}".format(
                        err=except_msg))

            self.I2C_ADDR = int(self.lcd_location, 16)
            self.lcd_init()
            self.lcd_string_write('Mycodo {}'.format(MYCODO_VERSION),
                                  self.LCD_LINE[1])
            self.lcd_string_write(u'Start {}'.format(self.lcd_name),
                                  self.LCD_LINE[2])
        except Exception as except_msg:
            self.logger.exception("Error: {err}".format(err=except_msg))
Beispiel #17
0
    def __init__(self, ready, input_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger(
            "mycodo.input_{id}".format(id=input_id))

        self.stop_iteration_counter = 0
        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.lock = {}
        self.measurement = None
        self.updateSuccess = False
        self.input_id = input_id
        self.control = DaemonControl()
        self.pause_loop = False
        self.verify_pause_loop = True

        self.cond_id = {}
        self.cond_action_id = {}
        self.cond_name = {}
        self.cond_is_activated = {}
        self.cond_if_input_period = {}
        self.cond_if_input_measurement = {}
        self.cond_if_input_edge_select = {}
        self.cond_if_input_edge_detected = {}
        self.cond_if_input_gpio_state = {}
        self.cond_if_input_direction = {}
        self.cond_if_input_setpoint = {}
        self.cond_do_output_id = {}
        self.cond_do_output_state = {}
        self.cond_do_output_duration = {}
        self.cond_execute_command = {}
        self.cond_email_notify = {}
        self.cond_do_lcd_id = {}
        self.cond_do_camera_id = {}
        self.cond_timer = {}
        self.smtp_wait_timer = {}

        self.setup_input_conditionals()

        input_dev = db_retrieve_table_daemon(Input, device_id=self.input_id)
        self.input_sel = input_dev
        self.unique_id = input_dev.unique_id
        self.i2c_bus = input_dev.i2c_bus
        self.location = input_dev.location
        self.power_output_id = input_dev.power_relay_id
        self.measurements = input_dev.measurements
        self.device = input_dev.device
        self.interface = input_dev.interface
        self.device_loc = input_dev.device_loc
        self.baud_rate = input_dev.baud_rate
        self.period = input_dev.period
        self.resolution = input_dev.resolution
        self.sensitivity = input_dev.sensitivity
        self.cmd_command = input_dev.cmd_command
        self.cmd_measurement = input_dev.cmd_measurement
        self.cmd_measurement_units = input_dev.cmd_measurement_units
        self.mux_address_raw = input_dev.multiplexer_address
        self.mux_bus = input_dev.multiplexer_bus
        self.mux_chan = input_dev.multiplexer_channel
        self.adc_chan = input_dev.adc_channel
        self.adc_gain = input_dev.adc_gain
        self.adc_resolution = input_dev.adc_resolution
        self.adc_measure = input_dev.adc_measure
        self.adc_measure_units = input_dev.adc_measure_units
        self.adc_volts_min = input_dev.adc_volts_min
        self.adc_volts_max = input_dev.adc_volts_max
        self.adc_units_min = input_dev.adc_units_min
        self.adc_units_max = input_dev.adc_units_max
        self.adc_inverse_unit_scale = input_dev.adc_inverse_unit_scale
        self.sht_clock_pin = input_dev.sht_clock_pin
        self.sht_voltage = input_dev.sht_voltage

        # Edge detection
        self.switch_edge = input_dev.switch_edge
        self.switch_bouncetime = input_dev.switch_bouncetime
        self.switch_reset_period = input_dev.switch_reset_period

        # PWM and RPM options
        self.weighting = input_dev.weighting
        self.rpm_pulses_per_rev = input_dev.rpm_pulses_per_rev
        self.sample_time = input_dev.sample_time

        # Output that will activate prior to input read
        self.pre_output_id = input_dev.pre_relay_id
        self.pre_output_duration = input_dev.pre_relay_duration
        self.pre_output_setup = False
        self.next_measurement = time.time()
        self.get_new_measurement = False
        self.trigger_cond = False
        self.measurement_acquired = False
        self.pre_output_activated = False
        self.pre_output_timer = time.time()

        output = db_retrieve_table_daemon(Output, entry='all')
        for each_output in output:  # Check if output ID actually exists
            if each_output.id == self.pre_output_id and self.pre_output_duration:
                self.pre_output_setup = True

        smtp = db_retrieve_table_daemon(SMTP, entry='first')
        self.smtp_max_count = smtp.hourly_max
        self.email_count = 0
        self.allowed_to_send_notice = True

        # Convert string I2C address to base-16 int
        if self.device in LIST_DEVICES_I2C:
            self.i2c_address = int(str(self.location), 16)

        # Set up multiplexer if enabled
        if self.device in LIST_DEVICES_I2C and self.mux_address_raw:
            self.mux_address_string = self.mux_address_raw
            self.mux_address = int(str(self.mux_address_raw), 16)
            self.mux_lock = "/var/lock/mycodo_multiplexer_0x{i2c:02X}.pid".format(
                i2c=self.mux_address)
            self.mux_lock = fasteners.InterProcessLock(self.mux_lock)
            self.mux_lock_acquired = False
            self.multiplexer = TCA9548A(self.mux_bus, self.mux_address)
        else:
            self.multiplexer = None

        # Set up edge detection of a GPIO pin
        if self.device == 'EDGE':
            if self.switch_edge == 'rising':
                self.switch_edge_gpio = GPIO.RISING
            elif self.switch_edge == 'falling':
                self.switch_edge_gpio = GPIO.FALLING
            else:
                self.switch_edge_gpio = GPIO.BOTH

        # Lock multiplexer, if it's enabled
        if self.multiplexer:
            self.lock_multiplexer()

        # Set up analog-to-digital converter
        if self.device in ['ADS1x15', 'MCP342x'] and self.location:
            self.adc_lock_file = "/var/lock/mycodo_adc_bus{bus}_0x{i2c:02X}.pid".format(
                bus=self.i2c_bus, i2c=self.i2c_address)

            if self.device == 'ADS1x15':
                self.adc = ADS1x15Read(self.i2c_address, self.i2c_bus,
                                       self.adc_chan, self.adc_gain)
            elif self.device == 'MCP342x':
                self.adc = MCP342xRead(self.i2c_address, self.i2c_bus,
                                       self.adc_chan, self.adc_gain,
                                       self.adc_resolution)
        else:
            self.adc = None

        self.device_recognized = True

        # Set up inputs or devices
        if self.device in ['EDGE', 'ADS1x15', 'MCP342x']:
            self.measure_input = None
        elif self.device == 'MYCODO_RAM':
            self.measure_input = MycodoRam()
        elif self.device == 'RPiCPULoad':
            self.measure_input = RaspberryPiCPULoad()
        elif self.device == 'RPi':
            self.measure_input = RaspberryPiCPUTemp()
        elif self.device == 'RPiFreeSpace':
            self.measure_input = RaspberryPiFreeSpace(self.location)
        elif self.device == 'AM2302':
            self.measure_input = DHT22Sensor(self.input_id, int(self.location))
        elif self.device == 'AM2315':
            self.measure_input = AM2315Sensor(self.input_id,
                                              self.i2c_bus,
                                              power=self.power_output_id)
        elif self.device == 'ATLAS_PH_I2C':
            self.measure_input = AtlaspHSensor(self.interface,
                                               i2c_address=self.i2c_address,
                                               i2c_bus=self.i2c_bus,
                                               sensor_sel=self.input_sel)
        elif self.device == 'ATLAS_PH_UART':
            self.measure_input = AtlaspHSensor(self.interface,
                                               device_loc=self.device_loc,
                                               baud_rate=self.baud_rate,
                                               sensor_sel=self.input_sel)
        elif self.device == 'ATLAS_PT1000_I2C':
            self.measure_input = AtlasPT1000Sensor(
                self.interface,
                i2c_address=self.i2c_address,
                i2c_bus=self.i2c_bus)
        elif self.device == 'ATLAS_PT1000_UART':
            self.measure_input = AtlasPT1000Sensor(self.interface,
                                                   device_loc=self.device_loc,
                                                   baud_rate=self.baud_rate)
        elif self.device == 'BH1750':
            self.measure_input = BH1750Sensor(self.i2c_address, self.i2c_bus,
                                              self.resolution,
                                              self.sensitivity)
        elif self.device == 'BME280':
            self.measure_input = BME280Sensor(self.i2c_address, self.i2c_bus)
        # TODO: BMP is an old designation and will be removed in the future
        elif self.device in ['BMP', 'BMP180']:
            self.measure_input = BMP180Sensor(self.i2c_bus)
        elif self.device == 'BMP280':
            self.measure_input = BMP280Sensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'CHIRP':
            self.measure_input = ChirpSensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'DS18B20':
            self.measure_input = DS18B20Sensor(self.location)
        elif self.device == 'DHT11':
            self.measure_input = DHT11Sensor(self.input_id,
                                             int(self.location),
                                             power=self.power_output_id)
        elif self.device == 'DHT22':
            self.measure_input = DHT22Sensor(self.input_id,
                                             int(self.location),
                                             power=self.power_output_id)
        elif self.device == 'HTU21D':
            self.measure_input = HTU21DSensor(self.i2c_bus)
        elif self.device == 'K30_UART':
            self.measure_input = K30Sensor(self.device_loc,
                                           baud_rate=self.baud_rate)
        elif self.device == 'MH_Z16_I2C':
            self.measure_input = MHZ16Sensor(self.interface,
                                             i2c_address=self.i2c_address,
                                             i2c_bus=self.i2c_bus)
        elif self.device == 'MH_Z16_UART':
            self.measure_input = MHZ16Sensor(self.interface,
                                             device_loc=self.device_loc,
                                             baud_rate=self.baud_rate)
        elif self.device == 'MH_Z19_UART':
            self.measure_input = MHZ19Sensor(self.device_loc,
                                             baud_rate=self.baud_rate)
        elif self.device == 'SHT1x_7x':
            self.measure_input = SHT1x7xSensor(int(self.location),
                                               self.sht_clock_pin,
                                               self.sht_voltage)
        elif self.device == 'SHT2x':
            self.measure_input = SHT2xSensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'SIGNAL_PWM':
            self.measure_input = SignalPWMInput(int(self.location),
                                                self.weighting,
                                                self.sample_time)
        elif self.device == 'SIGNAL_RPM':
            self.measure_input = SignalRPMInput(int(self.location),
                                                self.weighting,
                                                self.rpm_pulses_per_rev,
                                                self.sample_time)
        elif self.device == 'TMP006':
            self.measure_input = TMP006Sensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'TSL2561':
            self.measure_input = TSL2561Sensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'TSL2591':
            self.measure_input = TSL2591Sensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'LinuxCommand':
            self.measure_input = LinuxCommand(self.cmd_command,
                                              self.cmd_measurement)
        else:
            self.device_recognized = False
            self.logger.debug(
                "Device '{device}' not recognized".format(device=self.device))
            raise Exception("'{device}' is not a valid device type.".format(
                device=self.device))

        if self.multiplexer:
            self.unlock_multiplexer()

        self.edge_reset_timer = time.time()
        self.input_timer = time.time()
        self.running = False
        self.lastUpdate = None
Beispiel #18
0
    def check_conditionals(self, cond_id):
        """
        Check if any sensor conditional statements are activated and
        execute their actions if the conditional is true.

        For example, if measured temperature is above 30C, notify [email protected]

        :rtype: None

        :param cond_id: ID of conditional to check
        :type cond_id: str
        """
        logger_cond = logging.getLogger(
            "mycodo.sensor_cond_{id}".format(id=cond_id))
        attachment_file = False
        attachment_type = False

        cond = db_retrieve_table_daemon(Conditional,
                                        device_id=cond_id,
                                        entry='first')

        message = u"[Sensor Conditional: {name} ({id})]".format(name=cond.name,
                                                                id=cond_id)

        if cond.if_sensor_direction:
            last_measurement = self.get_last_measurement(
                cond.if_sensor_measurement)
            if (last_measurement
                    and ((cond.if_sensor_direction == 'above'
                          and last_measurement > cond.if_sensor_setpoint) or
                         (cond.if_sensor_direction == 'below'
                          and last_measurement < cond.if_sensor_setpoint))):

                message += u" {meas}: {value} ".format(
                    meas=cond.if_sensor_measurement, value=last_measurement)
                if cond.if_sensor_direction == 'above':
                    message += "(>"
                elif cond.if_sensor_direction == 'below':
                    message += "(<"
                message += u" {sp} set value).".format(
                    sp=cond.if_sensor_setpoint)
            else:
                logger_cond.debug("Last measurement not found")
                return 1
        elif cond.if_sensor_edge_detected:
            if cond.if_sensor_edge_select == 'edge':
                message += u" {edge} Edge Detected.".format(
                    edge=cond.if_sensor_edge_detected)
            elif cond.if_sensor_edge_select == 'state':
                if GPIO.input(int(self.location)) == cond.if_sensor_gpio_state:
                    message += u" {state} GPIO State Detected.".format(
                        state=cond.if_sensor_gpio_state)
                else:
                    return 0

        cond_actions = db_retrieve_table_daemon(ConditionalActions)
        cond_actions = cond_actions.filter(
            ConditionalActions.conditional_id == cond_id).all()

        for cond_action in cond_actions:
            message += u" Conditional Action ({id}): {do_action}.".format(
                id=cond_action.id, do_action=cond_action.do_action)

            # Actuate relay
            if (cond_action.do_relay_id
                    and cond_action.do_relay_state in ['on', 'off']):
                message += u" Turn relay {id} {state}".format(
                    id=cond_action.do_relay_id,
                    state=cond_action.do_relay_state)
                if (cond_action.do_relay_state == 'on'
                        and cond_action.do_relay_duration):
                    message += u" for {sec} seconds".format(
                        sec=cond_action.do_relay_duration)
                message += "."
                relay_on_off = threading.Thread(
                    target=self.control.relay_on_off,
                    args=(
                        cond_action.do_relay_id,
                        cond_action.do_relay_state,
                        cond_action.do_relay_duration,
                    ))
                relay_on_off.start()

            # Execute command in shell
            elif cond_action.do_action == 'command':
                message += u" Execute '{com}' ".format(
                    com=cond_action.do_action_string)
                _, _, cmd_status = cmd_output(cond_action.do_action_string)
                message += u"(Status: {stat}).".format(stat=cmd_status)

            # Capture photo
            elif cond_action.do_action in ['photo', 'photo_email']:
                message += u"  Capturing photo with camera ({id}).".format(
                    id=cond_action.do_camera_id)
                camera_still = db_retrieve_table_daemon(
                    Camera, device_id=cond_action.do_camera_id)
                attachment_file = camera_record('photo', camera_still)

            # Capture video
            elif cond_action.do_action in ['video', 'video_email']:
                message += u"  Capturing video with camera ({id}).".format(
                    id=cond_action.do_camera_id)
                camera_stream = db_retrieve_table_daemon(
                    Camera, device_id=cond_action.do_camera_id)
                attachment_file = camera_record(
                    'video',
                    camera_stream,
                    duration_sec=cond_action.do_camera_duration)

            # Activate PID controller
            elif cond_action.do_action == 'activate_pid':
                message += u" Activate PID ({id}).".format(
                    id=cond_action.do_pid_id)
                pid = db_retrieve_table_daemon(PID,
                                               device_id=cond_action.do_pid_id,
                                               entry='first')
                if pid.is_activated:
                    message += u" Notice: PID is already active!"
                else:
                    activate_pid = threading.Thread(
                        target=self.control.controller_activate,
                        args=(
                            'PID',
                            cond_action.do_pid_id,
                        ))
                    activate_pid.start()

            # Deactivate PID controller
            elif cond_action.do_action == 'deactivate_pid':
                message += u" Deactivate PID ({id}).".format(
                    id=cond_action.do_pid_id)
                pid = db_retrieve_table_daemon(PID,
                                               device_id=cond_action.do_pid_id,
                                               entry='first')
                if not pid.is_activated:
                    message += u" Notice: PID is already inactive!"
                else:
                    deactivate_pid = threading.Thread(
                        target=self.control.controller_deactivate,
                        args=(
                            'PID',
                            cond_action.do_pid_id,
                        ))
                    deactivate_pid.start()

            elif cond_action.do_action in [
                    'email', 'photo_email', 'video_email'
            ]:
                if (self.email_count >= self.smtp_max_count
                        and time.time() < self.smtp_wait_timer[cond_id]):
                    self.allowed_to_send_notice = False
                else:
                    if time.time() > self.smtp_wait_timer[cond_id]:
                        self.email_count = 0
                        self.smtp_wait_timer[cond_id] = time.time() + 3600
                    self.allowed_to_send_notice = True
                self.email_count += 1

                # If the emails per hour limit has not been exceeded
                if self.allowed_to_send_notice:
                    message += u" Notify {email}.".format(
                        email=cond_action.do_action_string)
                    # attachment_type != False indicates to
                    # attach a photo or video
                    if cond_action.do_action == 'photo_email':
                        message += u" Photo attached to email."
                        attachment_type = 'still'
                    elif cond_action.do_action == 'video_email':
                        message += u" Video attached to email."
                        attachment_type = 'video'

                    smtp = db_retrieve_table_daemon(SMTP, entry='first')
                    send_email(smtp.host, smtp.ssl, smtp.port, smtp.user,
                               smtp.passw, smtp.email_from,
                               cond_action.do_action_string, message,
                               attachment_file, attachment_type)
                else:
                    logger_cond.debug(
                        "Wait {sec:.0f} seconds to email again.".format(
                            sec=self.smtp_wait_timer[cond_id] - time.time()))

            elif cond_action.do_action == 'flash_lcd':
                message += u" Flashing LCD ({id}).".format(
                    id=cond_action.do_lcd_id)
                start_flashing = threading.Thread(
                    target=self.control.flash_lcd,
                    args=(
                        cond_action.do_lcd_id,
                        1,
                    ))
                start_flashing.start()

        logger_cond.debug(message)
Beispiel #19
0
    def __init__(self, ready, lcd_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger("mycodo.lcd_{id}".format(id=lcd_id))

        self.running = False
        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.flash_lcd_on = False
        self.lcd_initilized = False
        self.lcd_is_on = False
        self.lcd_id = lcd_id
        self.display_ids = []
        self.display_count = 0

        try:
            lcd = db_retrieve_table_daemon(LCD, device_id=self.lcd_id)
            self.lcd_name = lcd.name
            self.lcd_location = lcd.location
            self.lcd_i2c_bus = lcd.i2c_bus
            self.lcd_period = lcd.period
            self.lcd_x_characters = lcd.x_characters
            self.lcd_y_lines = lcd.y_lines
            self.timer = time.time() + self.lcd_period
            self.backlight_timer = time.time()

            if lcd.multiplexer_address:
                self.multiplexer_address_string = lcd.multiplexer_address
                self.multiplexer_address = int(str(lcd.multiplexer_address),
                                               16)
                self.multiplexer_channel = lcd.multiplexer_channel
                self.multiplexer = TCA9548A(self.multiplexer_address)
            else:
                self.multiplexer = None

            self.list_pids = ['setpoint', 'pid_time']
            self.list_outputs = ['duration_sec', 'relay_time', 'relay_state']

            self.list_inputs = MEASUREMENT_UNITS
            self.list_inputs.update(
                {'sensor_time': {
                    'unit': None,
                    'name': 'Time'
                }})

            # Add custom measurement and units to list (From linux command input)
            input_dev = db_retrieve_table_daemon(Input)
            self.list_inputs = add_custom_measurements(input_dev,
                                                       self.list_inputs,
                                                       MEASUREMENT_UNITS)

            lcd_data = db_retrieve_table_daemon(LCDData).filter(
                LCDData.lcd_id == lcd.id).all()

            self.lcd_string_line = {}
            self.lcd_line = {}
            self.lcd_max_age = {}

            for each_lcd_display in lcd_data:
                self.display_ids.append(each_lcd_display.id)
                self.lcd_string_line[each_lcd_display.id] = {}
                self.lcd_line[each_lcd_display.id] = {}
                self.lcd_max_age[each_lcd_display.id] = {}

                for i in range(1, self.lcd_y_lines + 1):
                    self.lcd_string_line[each_lcd_display.id][i] = ''
                    self.lcd_line[each_lcd_display.id][i] = {}
                    self.lcd_max_age[each_lcd_display.
                                     id][i] = each_lcd_display.line_1_max_age

                if self.lcd_y_lines in [2, 4]:
                    self.setup_lcd_line(each_lcd_display.id, 1,
                                        each_lcd_display.line_1_id,
                                        each_lcd_display.line_1_measurement)
                    self.setup_lcd_line(each_lcd_display.id, 2,
                                        each_lcd_display.line_2_id,
                                        each_lcd_display.line_2_measurement)

                if self.lcd_y_lines == 4:
                    self.setup_lcd_line(each_lcd_display.id, 3,
                                        each_lcd_display.line_3_id,
                                        each_lcd_display.line_3_measurement)
                    self.setup_lcd_line(each_lcd_display.id, 4,
                                        each_lcd_display.line_4_id,
                                        each_lcd_display.line_4_measurement)

            self.LCD_WIDTH = self.lcd_x_characters  # Max characters per line

            self.LCD_LINE = {1: 0x80, 2: 0xC0, 3: 0x94, 4: 0xD4}

            self.LCD_CHR = 1  # Mode - Sending data
            self.LCD_CMD = 0  # Mode - SenLCDding command

            self.LCD_BACKLIGHT = 0x08  # On
            self.LCD_BACKLIGHT_OFF = 0x00  # Off

            self.ENABLE = 0b00000100  # Enable bit

            # Timing constants
            self.E_PULSE = 0.0005
            self.E_DELAY = 0.0005

            # Setup I2C bus
            try:
                self.bus = smbus.SMBus(self.lcd_i2c_bus)
            except Exception as except_msg:
                self.logger.exception(
                    "Could not initialize I2C bus: {err}".format(
                        err=except_msg))

            self.I2C_ADDR = int(self.lcd_location, 16)
            self.lcd_init()

            if self.lcd_initilized:
                self.lcd_string_write('Mycodo {}'.format(MYCODO_VERSION),
                                      self.LCD_LINE[1])
                self.lcd_string_write(u'Start {}'.format(self.lcd_name),
                                      self.LCD_LINE[2])
        except Exception as except_msg:
            self.logger.exception("Error: {err}".format(err=except_msg))
Beispiel #20
0
    def check_conditionals(self, relay_id, on_duration):
        conditionals = db_retrieve_table_daemon(Conditional)
        conditionals = conditionals.filter(
            Conditional.if_relay_id == relay_id)
        conditionals = conditionals.filter(
            Conditional.is_activated == True)

        if self.is_on(relay_id):
            conditionals = conditionals.filter(
                Conditional.if_relay_state == 'on')
            conditionals = conditionals.filter(
                Conditional.if_relay_duration == on_duration)
        else:
            conditionals = conditionals.filter(
                Conditional.if_relay_state == 'off')

        for each_conditional in conditionals.all():
            conditional_actions = db_retrieve_table_daemon(ConditionalActions)
            conditional_actions = conditional_actions.filter(
                ConditionalActions.conditional_id == each_conditional.id).all()

            for each_cond_action in conditional_actions:
                now = time.time()
                timestamp = datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H-%M-%S')
                message = u"{ts}\n[Relay Conditional {id}] {name}\n".format(
                    ts=timestamp,
                    id=each_cond_action.id,
                    name=each_conditional.name)

                if each_cond_action.do_action == 'relay':
                    if each_cond_action.do_relay_id not in self.relay_name:
                        message += u"Error: Invalid relay ID {id}.".format(
                            id=each_cond_action.do_relay_id)
                    else:
                        message += u"If relay {id} ({name}) turns {state}, Then ".format(
                            id=each_conditional.if_relay_id,
                            name=self.relay_name[each_conditional.if_relay_id],
                            state=each_conditional.if_relay_state)
                        message += u"turn relay {id} ({name}) {state}".format(
                            id=each_cond_action.do_relay_id,
                            name=self.relay_name[each_cond_action.do_relay_id],
                            state=each_cond_action.do_relay_state)

                        if each_cond_action.do_relay_duration == 0:
                            self.relay_on_off(each_cond_action.do_relay_id,
                                              each_cond_action.do_relay_state)
                        else:
                            message += u" for {dur} seconds".format(
                                dur=each_cond_action.do_relay_duration)
                            self.relay_on_off(each_cond_action.do_relay_id,
                                              each_cond_action.do_relay_state,
                                              duration=each_cond_action.do_relay_duration)
                    message += ".\n"

                elif each_cond_action.do_action == 'command':
                    # Execute command as user mycodo
                    message += u"Execute: '{}'. ".format(
                        each_cond_action.do_action_string)
                    _, _, cmd_status = cmd_output(
                        each_cond_action.do_action_string)
                    message += u"Status: {}. ".format(cmd_status)

                elif each_cond_action.do_action == 'email':
                    if (self.email_count >= self.smtp_max_count and
                            time.time() < self.smtp_wait_time):
                        self.allowed_to_send_notice = False
                    else:
                        if time.time() > self.smtp_wait_time:
                            self.email_count = 0
                            self.smtp_wait_time = time.time() + 3600
                        self.allowed_to_send_notice = True
                    self.email_count += 1

                    if self.allowed_to_send_notice:
                        message += u"Notify {}.".format(
                            each_cond_action.email_notify)

                        smtp = db_retrieve_table_daemon(SMTP, entry='first')
                        send_email(
                            smtp.host, smtp.ssl, smtp.port, smtp.user,
                            smtp.passw, smtp.email_from,
                            each_cond_action.do_action_string, message)
                    else:
                        self.logger.debug(
                            "[Relay Conditional {}] True: {:.0f} seconds "
                            "left to be allowed to email again.".format(
                                each_conditional.id,
                                self.smtp_wait_time-time.time()))

                elif each_cond_action.do_action == 'flash_lcd':
                    start_flashing = threading.Thread(
                        target=self.control.flash_lcd,
                        args=(each_cond_action.do_lcd_id, 1,))
                    start_flashing.start()

                # TODO: Implement photo/video actions for relay conditionals
                elif each_cond_action.do_action == 'photo':
                    pass

                elif each_cond_action.do_action == 'video':
                    pass

                self.logger.debug(u"{}".format(message))
Beispiel #21
0
    def __init__(self, ready, sensor_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger(
            "mycodo.sensor_{id}".format(id=sensor_id))

        self.stop_iteration_counter = 0
        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.lock = {}
        self.measurement = None
        self.updateSuccess = False
        self.sensor_id = sensor_id
        self.control = DaemonControl()
        self.pause_loop = False
        self.verify_pause_loop = True

        self.cond_id = {}
        self.cond_action_id = {}
        self.cond_name = {}
        self.cond_is_activated = {}
        self.cond_if_sensor_period = {}
        self.cond_if_sensor_measurement = {}
        self.cond_if_sensor_edge_select = {}
        self.cond_if_sensor_edge_detected = {}
        self.cond_if_sensor_gpio_state = {}
        self.cond_if_sensor_direction = {}
        self.cond_if_sensor_setpoint = {}
        self.cond_do_relay_id = {}
        self.cond_do_relay_state = {}
        self.cond_do_relay_duration = {}
        self.cond_execute_command = {}
        self.cond_email_notify = {}
        self.cond_do_lcd_id = {}
        self.cond_do_camera_id = {}
        self.cond_timer = {}
        self.smtp_wait_timer = {}

        self.setup_sensor_conditionals()

        sensor = db_retrieve_table_daemon(Sensor, device_id=self.sensor_id)
        self.unique_id = sensor.unique_id
        self.i2c_bus = sensor.i2c_bus
        self.location = sensor.location
        self.power_relay_id = sensor.power_relay_id
        self.measurements = sensor.measurements
        self.device = sensor.device
        self.period = sensor.period
        self.resolution = sensor.resolution
        self.sensitivity = sensor.sensitivity
        self.mux_address_raw = sensor.multiplexer_address
        self.mux_bus = sensor.multiplexer_bus
        self.mux_chan = sensor.multiplexer_channel
        self.adc_chan = sensor.adc_channel
        self.adc_gain = sensor.adc_gain
        self.adc_resolution = sensor.adc_resolution
        self.adc_measure = sensor.adc_measure
        self.adc_measure_units = sensor.adc_measure_units
        self.adc_volts_min = sensor.adc_volts_min
        self.adc_volts_max = sensor.adc_volts_max
        self.adc_units_min = sensor.adc_units_min
        self.adc_units_max = sensor.adc_units_max
        self.sht_clock_pin = sensor.sht_clock_pin
        self.sht_voltage = sensor.sht_voltage

        # Edge detection
        self.switch_edge = sensor.switch_edge
        self.switch_bouncetime = sensor.switch_bouncetime
        self.switch_reset_period = sensor.switch_reset_period

        # Relay that will activate prior to sensor read
        self.pre_relay_id = sensor.pre_relay_id
        self.pre_relay_duration = sensor.pre_relay_duration
        self.pre_relay_setup = False
        self.next_measurement = time.time()
        self.get_new_measurement = False
        self.measurement_acquired = False
        self.pre_relay_activated = False
        self.pre_relay_timer = time.time()

        relay = db_retrieve_table_daemon(Relay, entry='all')
        for each_relay in relay:  # Check if relay ID actually exists
            if each_relay.id == self.pre_relay_id and self.pre_relay_duration:
                self.pre_relay_setup = True

        smtp = db_retrieve_table_daemon(SMTP, entry='first')
        self.smtp_max_count = smtp.hourly_max
        self.email_count = 0
        self.allowed_to_send_notice = True

        # Convert string I2C address to base-16 int
        if self.device in LIST_DEVICES_I2C:
            self.i2c_address = int(str(self.location), 16)

        # Set up multiplexer if enabled
        if self.device in LIST_DEVICES_I2C and self.mux_address_raw:
            self.mux_address_string = self.mux_address_raw
            self.mux_address = int(str(self.mux_address_raw), 16)
            self.mux_lock = "/var/lock/mycodo_multiplexer_0x{i2c:02X}.pid".format(
                i2c=self.mux_address)
            self.multiplexer = TCA9548A(self.mux_bus, self.mux_address)
        else:
            self.multiplexer = None

        if self.device in ['ADS1x15', 'MCP342x'] and self.location:
            self.adc_lock_file = "/var/lock/mycodo_adc_bus{bus}_0x{i2c:02X}.pid".format(
                bus=self.i2c_bus, i2c=self.i2c_address)

        # Set up edge detection of a GPIO pin
        if self.device == 'EDGE':
            if self.switch_edge == 'rising':
                self.switch_edge_gpio = GPIO.RISING
            elif self.switch_edge == 'falling':
                self.switch_edge_gpio = GPIO.FALLING
            else:
                self.switch_edge_gpio = GPIO.BOTH

        # Set up analog-to-digital converter
        elif self.device == 'ADS1x15':
            self.adc = ADS1x15Read(self.i2c_address, self.i2c_bus,
                                   self.adc_chan, self.adc_gain)
        elif self.device == 'MCP342x':
            self.adc = MCP342xRead(self.i2c_address, self.i2c_bus,
                                   self.adc_chan, self.adc_gain,
                                   self.adc_resolution)
        else:
            self.adc = None

        self.device_recognized = True

        # Set up sensors or devices
        if self.device in ['EDGE', 'ADS1x15', 'MCP342x']:
            self.measure_sensor = None
        elif self.device == 'MYCODO_RAM':
            self.measure_sensor = MycodoRam()
        elif self.device == 'RPiCPULoad':
            self.measure_sensor = RaspberryPiCPULoad()
        elif self.device == 'RPi':
            self.measure_sensor = RaspberryPiCPUTemp()
        elif self.device == 'RPiFreeSpace':
            self.measure_sensor = RaspberryPiFreeSpace(self.location)
        elif self.device == 'AM2302':
            self.measure_sensor = DHT22Sensor(self.sensor_id,
                                              int(self.location))
        elif self.device == 'AM2315':
            self.measure_sensor = AM2315Sensor(self.i2c_bus)
        elif self.device == 'ATLAS_PT1000':
            self.measure_sensor = AtlasPT1000Sensor(self.i2c_address,
                                                    self.i2c_bus)
        elif self.device == 'BH1750':
            self.measure_sensor = BH1750Sensor(self.i2c_address, self.i2c_bus,
                                               self.resolution,
                                               self.sensitivity)
        elif self.device == 'BME280':
            self.measure_sensor = BME280Sensor(self.i2c_address, self.i2c_bus)
        # TODO: BMP is an old designation and will be removed in the future
        elif self.device in ['BMP', 'BMP180']:
            self.measure_sensor = BMP180Sensor(self.i2c_bus)
        elif self.device == 'BMP280':
            self.measure_sensor = BMP280Sensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'CHIRP':
            self.measure_sensor = ChirpSensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'DS18B20':
            self.measure_sensor = DS18B20Sensor(self.location)
        elif self.device == 'DHT11':
            self.measure_sensor = DHT11Sensor(self.sensor_id,
                                              int(self.location),
                                              power=self.power_relay_id)
        elif self.device == 'DHT22':
            self.measure_sensor = DHT22Sensor(self.sensor_id,
                                              int(self.location),
                                              power=self.power_relay_id)
        elif self.device == 'HTU21D':
            self.measure_sensor = HTU21DSensor(self.i2c_bus)
        elif self.device == 'K30':
            self.measure_sensor = K30Sensor()
        elif self.device == 'SHT1x_7x':
            self.measure_sensor = SHT1x7xSensor(self.location,
                                                self.sht_clock_pin,
                                                self.sht_voltage)
        elif self.device == 'SHT2x':
            self.measure_sensor = SHT2xSensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'TMP006':
            self.measure_sensor = TMP006Sensor(self.i2c_address, self.i2c_bus)
        elif self.device == 'TSL2561':
            self.measure_sensor = TSL2561Sensor(self.i2c_address, self.i2c_bus)
        else:
            self.device_recognized = False
            self.logger.debug(
                "Device '{device}' not recognized".format(device=self.device))
            raise Exception("'{device}' is not a valid device type.".format(
                device=self.device))

        self.edge_reset_timer = time.time()
        self.sensor_timer = time.time()
        self.running = False
        self.lastUpdate = None
Beispiel #22
0
    def check_conditionals(self, output_id, state=None, on_duration=None, duty_cycle=None):
        conditionals = db_retrieve_table_daemon(Conditional)
        conditionals = conditionals.filter(
            Conditional.if_relay_id == output_id)
        conditionals = conditionals.filter(
            Conditional.is_activated == True)

        if self.is_on(output_id):
            conditionals = conditionals.filter(
                or_(Conditional.if_relay_state == 'on',
                    Conditional.if_relay_state == 'on_any'))

            on_with_duration = and_(
                Conditional.if_relay_state == 'on',
                Conditional.if_relay_duration == on_duration)
            conditionals = conditionals.filter(
                or_(Conditional.if_relay_state == 'on_any',
                    on_with_duration))

        else:
            conditionals = conditionals.filter(
                Conditional.if_relay_state == 'off')

        for each_conditional in conditionals.all():
            conditional_actions = db_retrieve_table_daemon(ConditionalActions)
            conditional_actions = conditional_actions.filter(
                ConditionalActions.conditional_id == each_conditional.id).all()

            for each_cond_action in conditional_actions:
                now = time.time()
                timestamp = datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H-%M-%S')
                message = u"{ts}\n[Output Conditional {id}] {name}\n".format(
                    ts=timestamp,
                    id=each_cond_action.id,
                    name=each_conditional.name)

                if each_cond_action.do_action == 'relay':
                    if each_cond_action.do_relay_id not in self.output_name:
                        message += u"Error: Invalid output ID {id}.".format(
                            id=each_cond_action.do_relay_id)
                    else:
                        message += u"If output {id} ({name}) turns {state}, Then ".format(
                            id=each_conditional.if_relay_id,
                            name=self.output_name[each_conditional.if_relay_id],
                            state=each_conditional.if_relay_state)
                        message += u"turn output {id} ({name}) {state}".format(
                            id=each_cond_action.do_relay_id,
                            name=self.output_name[each_cond_action.do_relay_id],
                            state=each_cond_action.do_relay_state)

                        if each_cond_action.do_relay_duration == 0:
                            self.output_on_off(each_cond_action.do_relay_id,
                                               each_cond_action.do_relay_state)
                        else:
                            message += u" for {dur} seconds".format(
                                dur=each_cond_action.do_relay_duration)
                            self.output_on_off(each_cond_action.do_relay_id,
                                               each_cond_action.do_relay_state,
                                               duration=each_cond_action.do_relay_duration)
                    message += ".\n"

                elif each_cond_action.do_action == 'command':
                    # Execute command as user mycodo
                    message += u"Execute: '{}'. ".format(
                        each_cond_action.do_action_string)

                    # Check command for variables to replace with values
                    command_str = each_cond_action.do_action_string
                    command_str = command_str.replace(
                        "((output_pin))", str(self.output_pin[output_id]))
                    command_str = command_str.replace(
                        "((output_action))", str(state))
                    command_str = command_str.replace(
                        "((output_duration))", str(on_duration))
                    command_str = command_str.replace(
                        "((output_pwm))", str(duty_cycle))
                    _, _, cmd_status = cmd_output(command_str)

                    message += u"Status: {}. ".format(cmd_status)

                elif each_cond_action.do_action == 'email':
                    if (self.email_count >= self.smtp_max_count and
                            time.time() < self.smtp_wait_time):
                        self.allowed_to_send_notice = False
                    else:
                        if time.time() > self.smtp_wait_time:
                            self.email_count = 0
                            self.smtp_wait_time = time.time() + 3600
                        self.allowed_to_send_notice = True
                    self.email_count += 1

                    if self.allowed_to_send_notice:
                        message += u"Notify {}.".format(
                            each_cond_action.email_notify)

                        smtp = db_retrieve_table_daemon(SMTP, entry='first')
                        send_email(
                            smtp.host, smtp.ssl, smtp.port, smtp.user,
                            smtp.passw, smtp.email_from,
                            each_cond_action.do_action_string, message)
                    else:
                        self.logger.debug(
                            "[Output Conditional {}] True: {:.0f} seconds "
                            "left to be allowed to email again.".format(
                                each_conditional.id,
                                self.smtp_wait_time-time.time()))

                elif each_cond_action.do_action == 'flash_lcd':
                    start_flashing = threading.Thread(
                        target=self.control.flash_lcd,
                        args=(each_cond_action.do_lcd_id, 1,))
                    start_flashing.start()

                # TODO: Implement photo/video actions for output conditionals
                elif each_cond_action.do_action == 'photo':
                    self.logger.error("Photo action not currently implemented")

                elif each_cond_action.do_action == 'video':
                    self.logger.error("Video action not currently implemented")

                self.logger.debug(u"{}".format(message))
Beispiel #23
0
    def get_lcd_strings(self):
        """
        Retrieve measurements and/or timestamps and create strings for LCDs
        If no data is retrieveable, create string "NO DATA RETURNED".
        """
        # loop to acquire all measurements required to be displayed on the LCD
        for i in range(1, self.lcd_y_lines + 1):
            if self.lcd_line[i]['id']:
                # Get latest measurement (within past minute) from influxdb
                # FROM '/.*/' returns any measurement (for grabbing time of last measurement)
                last_measurement_success = False
                try:
                    if self.lcd_line[i]['measurement'] == 'relay_state':
                        self.lcd_line[i][
                            'measurement_value'] = self.relay_state(
                                self.lcd_line[i]['id'])
                        last_measurement_success = True
                    else:
                        if self.lcd_line[i]['measurement'] == 'time':
                            last_measurement = read_last_influxdb(
                                self.lcd_line[i]['id'], '/.*/')
                        else:
                            last_measurement = read_last_influxdb(
                                self.lcd_line[i]['id'],
                                self.lcd_line[i]['measurement'])
                        if last_measurement:
                            self.lcd_line[i]['time'] = last_measurement[0]
                            self.lcd_line[i][
                                'measurement_value'] = last_measurement[1]
                            utc_dt = datetime.datetime.strptime(
                                self.lcd_line[i]['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 {}: {} @ {}".format(
                                self.lcd_line[i]['measurement'],
                                self.lcd_line[i]['measurement_value'],
                                local_timestamp))
                            last_measurement_success = True
                        else:
                            self.lcd_line[i]['time'] = None
                            self.lcd_line[i]['measurement_value'] = None
                            self.logger.debug("No data returned from "
                                              "influxdb")
                except Exception as except_msg:
                    self.logger.debug(
                        "Failed to read measurement from the "
                        "influxdb database: {err}".format(err=except_msg))

                try:
                    if last_measurement_success:
                        # Determine if the LCD output will have a value unit
                        measurement = ''
                        if self.lcd_line[i]['measurement'] == 'setpoint':
                            pid = db_retrieve_table_daemon(
                                PID, unique_id=self.lcd_line[i]['id'])
                            measurement = pid.measurement
                        elif self.lcd_line[i]['measurement'] == 'duration_sec':
                            measurement = 'duration_sec'
                            self.lcd_line[i][
                                'measurement_value'] = '{:.2f}'.format(
                                    self.lcd_line[i]['measurement_value'])
                        elif self.lcd_line[i][
                                'measurement'] in MEASUREMENT_UNITS:
                            measurement = self.lcd_line[i]['measurement']

                        # Produce the line that will be displayed on the LCD
                        number_characters = self.lcd_x_characters
                        if self.lcd_line[i]['measurement'] == 'time':
                            # Convert UTC timestamp to local timezone
                            utc_dt = datetime.datetime.strptime(
                                self.lcd_line[i]['time'].split(".")[0],
                                '%Y-%m-%dT%H:%M:%S')
                            utc_timestamp = calendar.timegm(utc_dt.timetuple())
                            self.lcd_string_line[i] = str(
                                datetime.datetime.fromtimestamp(utc_timestamp))
                        elif measurement:
                            value_length = len(
                                str(self.lcd_line[i]['measurement_value']))
                            unit_length = len(
                                MEASUREMENT_UNITS[measurement]['unit'])
                            name_length = number_characters - value_length - unit_length - 2
                            name_cropped = self.lcd_line[i]['name'].ljust(
                                name_length)[:name_length]
                            self.lcd_string_line[
                                i] = u'{name} {value} {unit}'.format(
                                    name=name_cropped,
                                    value=self.lcd_line[i]
                                    ['measurement_value'],
                                    unit=MEASUREMENT_UNITS[measurement]
                                    ['unit'])
                        else:
                            value_length = len(
                                str(self.lcd_line[i]['measurement_value']))
                            name_length = number_characters - value_length - 1
                            name_cropped = self.lcd_line[i][
                                'name'][:name_length]
                            self.lcd_string_line[i] = u'{name} {value}'.format(
                                name=name_cropped,
                                value=self.lcd_line[i]['measurement_value'])
                    else:
                        self.lcd_string_line[i] = 'ERROR: NO DATA'
                except Exception as except_msg:
                    self.logger.exception(
                        "Error: {err}".format(err=except_msg))
            else:
                self.lcd_string_line[i] = ''
Beispiel #24
0
    def __init__(self, ready, pid_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger("mycodo.pid_{id}".format(id=pid_id))

        self.running = False
        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.pid_id = pid_id
        self.pid_unique_id = db_retrieve_table_daemon(
            PID, device_id=self.pid_id).unique_id
        self.control = DaemonControl()

        self.control_variable = 0.0
        self.derivator = 0.0
        self.integrator = 0.0
        self.error = 0.0
        self.P_value = None
        self.I_value = None
        self.D_value = None
        self.set_point = 0.0
        self.lower_seconds_on = 0.0
        self.raise_seconds_on = 0.0
        self.lower_duty_cycle = 0.0
        self.raise_duty_cycle = 0.0
        self.last_time = None
        self.last_measurement = None
        self.last_measurement_success = False

        self.is_activated = None
        self.is_held = None
        self.is_paused = None
        self.measurement = None
        self.method_id = None
        self.direction = None
        self.raise_output_id = None
        self.raise_min_duration = None
        self.raise_max_duration = None
        self.raise_min_off_duration = None
        self.lower_output_id = None
        self.lower_min_duration = None
        self.lower_max_duration = None
        self.lower_min_off_duration = None
        self.Kp = None
        self.Ki = None
        self.Kd = None
        self.integrator_min = None
        self.integrator_max = None
        self.period = None
        self.max_measure_age = None
        self.default_set_point = None
        self.set_point = None

        self.input_unique_id = None
        self.input_duration = None

        self.raise_output_type = None
        self.lower_output_type = None

        self.initialize_values()

        self.timer = t.time() + self.period

        # Check if a method is set for this PID
        self.method_start_act = None
        if self.method_id:
            method = db_retrieve_table_daemon(Method, device_id=self.method_id)
            method_data = db_retrieve_table_daemon(MethodData)
            method_data = method_data.filter(
                MethodData.method_id == self.method_id)
            method_data_repeat = method_data.filter(
                MethodData.duration_sec == 0).first()
            pid = db_retrieve_table_daemon(PID, device_id=self.pid_id)
            self.method_type = method.method_type
            self.method_start_act = pid.method_start_time
            self.method_start_time = None
            self.method_end_time = None

            if self.method_type == 'Duration':
                if self.method_start_act == 'Ended':
                    # Method has ended and hasn't been instructed to begin again
                    pass
                elif self.method_start_act == 'Ready' or self.method_start_act is None:
                    # Method has been instructed to begin
                    now = datetime.datetime.now()
                    self.method_start_time = now
                    if method_data_repeat and method_data_repeat.duration_end:
                        self.method_end_time = now + datetime.timedelta(
                            seconds=float(method_data_repeat.duration_end))

                    with session_scope(MYCODO_DB_PATH) as db_session:
                        mod_pid = db_session.query(PID)
                        mod_pid = mod_pid.filter(PID.id == self.pid_id).first()
                        mod_pid.method_start_time = self.method_start_time
                        mod_pid.method_end_time = self.method_end_time
                        db_session.commit()
                else:
                    # Method neither instructed to begin or not to
                    # Likely there was a daemon restart ot power failure
                    # Resume method with saved start_time
                    self.method_start_time = datetime.datetime.strptime(
                        str(pid.method_start_time), '%Y-%m-%d %H:%M:%S.%f')
                    if method_data_repeat and method_data_repeat.duration_end:
                        self.method_end_time = datetime.datetime.strptime(
                            str(pid.method_end_time), '%Y-%m-%d %H:%M:%S.%f')
                        if self.method_end_time > datetime.datetime.now():
                            self.logger.warning(
                                "Resuming method {id}: started {start}, "
                                "ends {end}".format(
                                    id=self.method_id,
                                    start=self.method_start_time,
                                    end=self.method_end_time))
                        else:
                            self.method_start_act = 'Ended'
                    else:
                        self.method_start_act = 'Ended'
Beispiel #25
0
    def output_on_off(self, output_id, state,
                     duration=0.0,
                     min_off=0.0,
                     duty_cycle=0.0,
                     trigger_conditionals=True):
        """
        Turn a output on or off
        The GPIO may be either HIGH or LOW to activate a output. This trigger
        state will be referenced to determine if the GPIO needs to be high or
        low to turn the output on or off.

        Conditionals will be checked for each action requested of a output, and
        if true, those conditional actions will be executed. For example:
            'If output 1 turns on, turn output 3 off'

        :param output_id: Unique ID for output
        :type output_id: int
        :param state: What state is desired? 'on' or 'off'
        :type state: str
        :param duration: If state is 'on', a duration can be set to turn the output off after
        :type duration: float
        :param min_off: Don't turn on if not off for at least this duration (0 = disabled)
        :type min_off: float
        :param duty_cycle: Duty cycle of PWM output
        :type duty_cycle: float
        :param trigger_conditionals: Whether to trigger conditionals to act or not
        :type trigger_conditionals: bool
        """
        # Check if output exists
        output_id = int(output_id)
        if output_id not in self.output_id:
            self.logger.warning(
                "Cannot turn {state} Output with ID {id}. "
                "It doesn't exist".format(
                    state=state, id=output_id))
            return 1

        # Signaled to turn output on
        if state == 'on':

            # Check if pin is valid
            if (self.output_type[output_id] in ['pwm',
                                              'wired',
                                              'wireless_433MHz_pi_switch'] and
                    self.output_pin[output_id] is None):
                self.logger.warning(
                    u"Invalid pin for output {id} ({name}): {pin}.".format(
                        id=self.output_id[output_id],
                        name=self.output_name[output_id],
                        pin=self.output_pin[output_id]))
                return 1

            # Check if max amperage will be exceeded
            if self.output_type[output_id] in ['command',
                                             'wired',
                                             'wireless_433MHz_pi_switch']:
                current_amps = self.current_amp_load()
                max_amps = db_retrieve_table_daemon(Misc, entry='first').max_amps
                if current_amps + self.output_amps[output_id] > max_amps:
                    self.logger.warning(
                        u"Cannot turn output {} ({}) On. If this output turns on, "
                        u"there will be {} amps being drawn, which exceeds the "
                        u"maximum set draw of {} amps.".format(
                            self.output_id[output_id],
                            self.output_name[output_id],
                            current_amps,
                            max_amps))
                    return 1

                # If the output is used in a PID, a minimum off duration is set,
                # and if the off duration has surpassed that amount of time (i.e.
                # has it been off for longer then the minimum off duration?).
                current_time = datetime.datetime.now()
                if (min_off and not self.is_on(output_id) and
                        current_time > self.output_on_until[output_id]):
                    off_seconds = (current_time - self.output_on_until[output_id]).total_seconds()
                    if off_seconds < min_off:
                        self.logger.debug(
                            u"Output {id} ({name}) instructed to turn on by PID, "
                            u"however the minimum off period of {min_off_sec} "
                            u"seconds has not been reached yet (it has only been "
                            u"off for {off_sec} seconds).".format(
                                id=self.output_id[output_id],
                                name=self.output_name[output_id],
                                min_off_sec=min_off,
                                off_sec=off_seconds))
                        return 1

            # Turn output on for a duration
            if (self.output_type[output_id] in ['command',
                                              'wired',
                                              'wireless_433MHz_pi_switch'] and
                    duration):
                time_now = datetime.datetime.now()
                if self.is_on(output_id) and self.output_on_duration[output_id]:
                    if self.output_on_until[output_id] > time_now:
                        remaining_time = (self.output_on_until[output_id] - time_now).total_seconds()
                    else:
                        remaining_time = 0
                    time_on = self.output_last_duration[output_id] - remaining_time
                    self.logger.debug(
                        u"Output {rid} ({rname}) is already on for a duration "
                        u"of {ron:.2f} seconds (with {rremain:.2f} seconds "
                        u"remaining). Recording the amount of time the output "
                        u"has been on ({rbeenon:.2f} sec) and updating the on "
                        u"duration to {rnewon:.2f} seconds.".format(
                            rid=self.output_id[output_id],
                            rname=self.output_name[output_id],
                            ron=self.output_last_duration[output_id],
                            rremain=remaining_time,
                            rbeenon=time_on,
                            rnewon=duration))
                    self.output_on_until[output_id] = time_now + datetime.timedelta(seconds=duration)
                    self.output_last_duration[output_id] = duration

                    if time_on > 0:
                        # Write the duration the output was ON to the
                        # database at the timestamp it turned ON
                        duration = float(time_on)
                        timestamp = datetime.datetime.utcnow() - datetime.timedelta(seconds=duration)
                        write_db = threading.Thread(
                            target=write_influxdb_value,
                            args=(self.output_unique_id[output_id],
                                  'duration_sec',
                                  duration,
                                  timestamp,))
                        write_db.start()

                    return 0
                elif self.is_on(output_id) and not self.output_on_duration:
                    self.output_on_duration[output_id] = True
                    self.output_on_until[output_id] = time_now + datetime.timedelta(seconds=duration)
                    self.output_last_duration[output_id] = duration
                    self.logger.debug(
                        u"Output {id} ({name}) is currently on without a "
                        u"duration. Turning into a duration  of {dur:.1f} "
                        u"seconds.".format(
                            id=self.output_id[output_id],
                            name=self.output_name[output_id],
                            dur=duration))
                    return 0
                else:
                    self.output_on_until[output_id] = time_now + datetime.timedelta(seconds=duration)
                    self.output_on_duration[output_id] = True
                    self.output_last_duration[output_id] = duration
                    self.logger.debug(
                        u"Output {id} ({name}) on for {dur:.1f} "
                        u"seconds.".format(
                            id=self.output_id[output_id],
                            name=self.output_name[output_id],
                            dur=duration))
                    self.output_switch(output_id, 'on')

            # Just turn output on
            elif self.output_type[output_id] in ['command',
                                               'wired',
                                               'wireless_433MHz_pi_switch']:
                if self.is_on(output_id):
                    self.logger.warning(
                        u"Output {id} ({name}) is already on.".format(
                            id=self.output_id[output_id],
                            name=self.output_name[output_id]))
                    return 1
                else:
                    # Record the time the output was turned on in order to
                    # calculate and log the total duration is was on, when
                    # it eventually turns off.
                    self.output_time_turned_on[output_id] = datetime.datetime.now()
                    self.logger.debug(
                        u"Output {id} ({name}) ON at {timeon}.".format(
                            id=self.output_id[output_id],
                            name=self.output_name[output_id],
                            timeon=self.output_time_turned_on[output_id]))
                    self.output_switch(output_id, 'on')

            # PWM output
            elif self.output_type[output_id] == 'pwm':
                # Record the time the PWM was turned on
                if self.pwm_hertz[output_id] <= 0:
                    self.logger.warning(u"PWM Hertz must be a positive value")
                    return 1
                self.pwm_time_turned_on[output_id] = datetime.datetime.now()
                self.logger.debug(
                    u"PWM {id} ({name}) ON with a duty cycle of {dc:.2f}% at {hertz} Hz".format(
                        id=self.output_id[output_id],
                        name=self.output_name[output_id],
                        dc=abs(duty_cycle),
                        hertz=self.pwm_hertz[output_id]))
                self.output_switch(output_id, 'on', duty_cycle=duty_cycle)

                # Write the duty cycle of the PWM to the database
                write_db = threading.Thread(
                    target=write_influxdb_value,
                    args=(self.output_unique_id[output_id],
                          'duty_cycle',
                          duty_cycle,))
                write_db.start()

        # Signaled to turn output off
        elif state == 'off':
            if not self._is_setup(output_id):
                return
            if (self.output_type[output_id] in ['pwm',
                                              'wired',
                                              'wireless_433MHz_pi_switch'] and
                    self.output_pin[output_id] is None):
                return

            self.output_switch(output_id, 'off')

            self.logger.debug(u"Output {id} ({name}) turned off.".format(
                    id=self.output_id[output_id],
                    name=self.output_name[output_id]))

            # Write PWM duty cycle to database
            if (self.output_type[output_id] == 'pwm' and
                    self.pwm_time_turned_on[output_id] is not None):
                # Write the duration the PWM was ON to the database
                # at the timestamp it turned ON
                duration = (datetime.datetime.now() - self.pwm_time_turned_on[output_id]).total_seconds()
                self.pwm_time_turned_on[output_id] = None
                timestamp = datetime.datetime.utcnow() - datetime.timedelta(seconds=duration)
                write_db = threading.Thread(
                    target=write_influxdb_value,
                    args=(self.output_unique_id[output_id],
                          'duty_cycle',
                          duty_cycle,
                          timestamp,))
                write_db.start()

            # Write output duration on to database
            elif (self.output_time_turned_on[output_id] is not None or
                    self.output_on_duration[output_id]):
                duration = 0
                if self.output_on_duration[output_id]:
                    remaining_time = 0
                    time_now = datetime.datetime.now()
                    if self.output_on_until[output_id] > time_now:
                        remaining_time = (self.output_on_until[output_id] - time_now).total_seconds()
                    duration = self.output_last_duration[output_id] - remaining_time
                    self.output_on_duration[output_id] = False
                    self.output_on_until[output_id] = datetime.datetime.now()

                if self.output_time_turned_on[output_id] is not None:
                    # Write the duration the output was ON to the database
                    # at the timestamp it turned ON
                    duration = (datetime.datetime.now() - self.output_time_turned_on[output_id]).total_seconds()
                    self.output_time_turned_on[output_id] = None

                timestamp = datetime.datetime.utcnow() - datetime.timedelta(seconds=duration)
                write_db = threading.Thread(
                    target=write_influxdb_value,
                    args=(self.output_unique_id[output_id],
                          'duration_sec',
                          duration,
                          timestamp,))
                write_db.start()

        if trigger_conditionals:
            self.check_conditionals(output_id,
                                    state=state,
                                    on_duration=duration,
                                    duty_cycle=duty_cycle)
Beispiel #26
0
    def manipulate_relays(self):
        """
        Activate a relay based on PID output (control variable) and whether
        the manipulation directive is to raise, lower, or both.

        :rtype: None
        """
        # If the last measurement was able to be retrieved and was entered within the past minute
        if self.last_measurement_success:
            #
            # PID control variable is positive, indicating a desire to raise
            # the environmental condition
            #
            if self.direction in ['raise', 'both'] and self.raise_relay_id:
                if self.control_variable > 0:
                    # Ensure the relay on duration doesn't exceed the set maximum
                    if (self.raise_max_duration and
                            self.control_variable > self.raise_max_duration):
                        self.raise_seconds_on = self.raise_max_duration
                    else:
                        self.raise_seconds_on = float("{0:.2f}".format(
                            self.control_variable))

                    # Turn off lower_relay if active, because we're now raising
                    if self.lower_relay_id:
                        relay = db_retrieve_table_daemon(
                            Relay, device_id=self.lower_relay_id)
                        if relay.is_on():
                            self.control.relay_off(self.lower_relay_id)

                    if self.raise_seconds_on > self.raise_min_duration:
                        # Activate raise_relay for a duration
                        self.logger.debug(
                            "Setpoint: {sp} Output: {op} to relay "
                            "{relay}".format(sp=self.set_point,
                                             op=self.control_variable,
                                             relay=self.raise_relay_id))
                        self.control.relay_on(
                            self.raise_relay_id,
                            self.raise_seconds_on,
                            min_off_duration=self.raise_min_off_duration)
                else:
                    self.control.relay_off(self.raise_relay_id)

            #
            # PID control variable is negative, indicating a desire to lower
            # the environmental condition
            #
            if self.direction in ['lower', 'both'] and self.lower_relay_id:
                if self.control_variable < 0:
                    # Ensure the relay on duration doesn't exceed the set maximum
                    if (self.lower_max_duration and abs(self.control_variable)
                            > self.lower_max_duration):
                        self.lower_seconds_on = self.lower_max_duration
                    else:
                        self.lower_seconds_on = abs(
                            float("{0:.2f}".format(self.control_variable)))

                    # Turn off raise_relay if active, because we're now lowering
                    if self.raise_relay_id:
                        relay = db_retrieve_table_daemon(
                            Relay, device_id=self.raise_relay_id)
                        if relay.is_on():
                            self.control.relay_off(self.raise_relay_id)

                    if self.lower_seconds_on > self.lower_min_duration:
                        # Activate lower_relay for a duration
                        self.logger.debug("Setpoint: {sp} Output: {op} to "
                                          "relay {relay}".format(
                                              sp=self.set_point,
                                              op=self.control_variable,
                                              relay=self.lower_relay_id))
                        self.control.relay_on(
                            self.lower_relay_id,
                            self.lower_seconds_on,
                            min_off_duration=self.lower_min_off_duration)
                else:
                    self.control.relay_off(self.lower_relay_id)

        else:
            if self.direction in ['raise', 'both'] and self.raise_relay_id:
                self.control.relay_off(self.raise_relay_id)
            if self.direction in ['lower', 'both'] and self.lower_relay_id:
                self.control.relay_off(self.lower_relay_id)
Beispiel #27
0
    def run(self):
        try:
            self.running = True
            self.logger.info("Activated in {:.1f} ms".format(
                (timeit.default_timer() - self.thread_startup_timer) * 1000))
            if self.is_paused:
                self.logger.info("Paused")
            elif self.is_held:
                self.logger.info("Held")
            self.ready.set()

            while self.running:

                if (self.method_start_act == 'Ended'
                        and self.method_type == 'Duration'):
                    self.stop_controller(ended_normally=False,
                                         deactivate_pid=True)
                    self.logger.warning(
                        "Method has ended. "
                        "Activate the PID controller to start it again.")

                elif t.time() > self.timer:
                    # Ensure the timer ends in the future
                    while t.time() > self.timer:
                        self.timer = self.timer + self.period

                    # If PID is active, retrieve input measurement and update PID output
                    if self.is_activated and not self.is_paused:
                        self.get_last_measurement()

                        if self.last_measurement_success:
                            # Update setpoint using a method if one is selected
                            if self.method_id:
                                this_controller = db_retrieve_table_daemon(
                                    PID, device_id=self.pid_id)
                                setpoint, ended = calculate_method_setpoint(
                                    self.method_id, PID, this_controller,
                                    Method, MethodData, self.logger)
                                if ended:
                                    self.method_start_act = 'Ended'
                                if setpoint is not None:
                                    self.set_point = setpoint
                                else:
                                    self.set_point = self.default_set_point

                            write_setpoint_db = threading.Thread(
                                target=write_influxdb_value,
                                args=(
                                    self.pid_unique_id,
                                    'setpoint',
                                    self.set_point,
                                ))
                            write_setpoint_db.start()

                            # Update PID and get control variable
                            self.control_variable = self.update_pid_output(
                                self.last_measurement)

                    # If PID is active or on hold, activate outputs
                    if ((self.is_activated and not self.is_paused)
                            or (self.is_activated and self.is_held)):
                        self.manipulate_output()
                t.sleep(0.1)

            # Turn off output used in PID when the controller is deactivated
            if self.raise_output_id and self.direction in ['raise', 'both']:
                self.control.relay_off(self.raise_output_id,
                                       trigger_conditionals=True)
            if self.lower_output_id and self.direction in ['lower', 'both']:
                self.control.relay_off(self.lower_output_id,
                                       trigger_conditionals=True)

            self.running = False
            self.logger.info("Deactivated in {:.1f} ms".format(
                (timeit.default_timer() - self.thread_shutdown_timer) * 1000))
        except Exception as except_msg:
            self.logger.exception("Run Error: {err}".format(err=except_msg))
Beispiel #28
0
    def __init__(self, ready, timer_id):
        threading.Thread.__init__(self)

        self.logger = logging.getLogger(
            "mycodo.timer_{id}".format(id=timer_id))

        self.thread_startup_timer = timeit.default_timer()
        self.thread_shutdown_timer = 0
        self.ready = ready
        self.timer_id = timer_id
        self.control = DaemonControl()

        timer = db_retrieve_table_daemon(Timer, device_id=self.timer_id)
        self.timer_type = timer.timer_type
        self.output_unique_id = timer.relay_id
        self.method_id = timer.method_id
        self.method_period = timer.method_period
        self.state = timer.state
        self.time_start = timer.time_start
        self.time_end = timer.time_end
        self.duration_on = timer.duration_on
        self.duration_off = timer.duration_off

        self.output_id = db_retrieve_table_daemon(
            Output, unique_id=self.output_unique_id).id

        # Time of day split into hour and minute
        if self.time_start:
            time_split = self.time_start.split(":")
            self.start_hour = time_split[0]
            self.start_minute = time_split[1]
        else:
            self.start_hour = None
            self.start_minute = None

        if self.time_end:
            time_split = self.time_end.split(":")
            self.end_hour = time_split[0]
            self.end_minute = time_split[1]
        else:
            self.end_hour = None
            self.end_minute = None

        self.duration_timer = time.time()
        self.pwm_method_timer = time.time()
        self.date_timer_not_executed = True
        self.running = False

        if self.method_id:
            method = db_retrieve_table_daemon(Method, device_id=self.method_id)
            method_data = db_retrieve_table_daemon(MethodData)
            method_data = method_data.filter(
                MethodData.method_id == self.method_id)
            method_data_repeat = method_data.filter(
                MethodData.duration_sec == 0).first()
            self.method_type = method.method_type
            self.method_start_act = timer.method_start_time
            self.method_start_time = None
            self.method_end_time = None

            if self.method_type == 'Duration':
                if self.method_start_act == 'Ended':
                    self.stop_controller(ended_normally=False,
                                         deactivate_timer=True)
                    self.logger.warning(
                        "Method has ended. "
                        "Activate the Timer controller to start it again.")
                elif self.method_start_act == 'Ready' or self.method_start_act is None:
                    # Method has been instructed to begin
                    now = datetime.datetime.now()
                    self.method_start_time = now
                    if method_data_repeat and method_data_repeat.duration_end:
                        self.method_end_time = now + datetime.timedelta(
                            seconds=float(method_data_repeat.duration_end))

                    with session_scope(MYCODO_DB_PATH) as db_session:
                        mod_timer = db_session.query(Timer)
                        mod_timer = mod_timer.filter(
                            Timer.id == self.timer_id).first()
                        mod_timer.method_start_time = self.method_start_time
                        mod_timer.method_end_time = self.method_end_time
                        db_session.commit()
            else:
                # Method neither instructed to begin or not to
                # Likely there was a daemon restart ot power failure
                # Resume method with saved start_time
                self.method_start_time = datetime.datetime.strptime(
                    str(timer.method_start_time), '%Y-%m-%d %H:%M:%S.%f')
                if method_data_repeat and method_data_repeat.duration_end:
                    self.method_end_time = datetime.datetime.strptime(
                        str(timer.method_end_time), '%Y-%m-%d %H:%M:%S.%f')
                    if self.method_end_time > datetime.datetime.now():
                        self.logger.warning(
                            "Resuming method {id}: started {start}, "
                            "ends {end}".format(id=self.method_id,
                                                start=self.method_start_time,
                                                end=self.method_end_time))
                    else:
                        self.method_start_act = 'Ended'
                else:
                    self.method_start_act = 'Ended'
Beispiel #29
0
    def run(self):
        self.running = True
        self.logger.info("Activated in {:.1f} ms".format(
            (timeit.default_timer() - self.thread_startup_timer) * 1000))
        self.ready.set()

        while self.running:

            # Timer is set to react at a specific hour and minute of the day
            if self.timer_type == 'time':
                if (int(self.start_hour) == datetime.datetime.now().hour
                        and int(self.start_minute)
                        == datetime.datetime.now().minute):
                    # Ensure this is triggered only once at this specific time
                    if self.date_timer_not_executed:
                        self.date_timer_not_executed = False
                        message = "At {st}, turn Output {id} {state}".format(
                            st=self.time_start,
                            id=self.output_id,
                            state=self.state)
                        if self.state == 'on' and self.duration_on:
                            message += " for {sec} seconds".format(
                                sec=self.duration_on)
                        else:
                            self.duration_on = 0
                        self.logger.debug(message)

                        modulate_output = threading.Thread(
                            target=self.control.output_on_off,
                            args=(
                                self.output_id,
                                self.state,
                            ),
                            kwargs={'duration': self.duration_on})
                        modulate_output.start()
                elif not self.date_timer_not_executed:
                    self.date_timer_not_executed = True

            # Timer is set to react at a specific time duration of the day
            elif self.timer_type == 'timespan':
                if time_between_range(self.time_start, self.time_end):
                    current_output_state = self.control.relay_state(
                        self.output_id)
                    if self.state != current_output_state:
                        message = "Output {output} should be {state}, but is " \
                                  "{cstate}. Turning {state}.".format(
                                    output=self.output_id,
                                    state=self.state,
                                    cstate=current_output_state)
                        modulate_output = threading.Thread(
                            target=self.control.output_on_off,
                            args=(
                                self.output_id,
                                self.state,
                            ))
                        modulate_output.start()
                        self.logger.debug(message)

            # Timer is a simple on/off duration timer
            elif self.timer_type == 'duration':
                if time.time() > self.duration_timer:
                    self.duration_timer = (time.time() + self.duration_on +
                                           self.duration_off)
                    self.logger.debug("Turn Output {output} on for {onsec} "
                                      "seconds, then off for {offsec} "
                                      "seconds".format(
                                          output=self.output_id,
                                          onsec=self.duration_on,
                                          offsec=self.duration_off))
                    output_on = threading.Thread(target=self.control.relay_on,
                                                 args=(
                                                     self.output_id,
                                                     self.duration_on,
                                                 ))
                    output_on.start()

            # Timer is a PWM Method timer
            elif self.timer_type == 'pwm_method':
                try:
                    if time.time() > self.pwm_method_timer:
                        if self.method_start_act == 'Ended':
                            self.stop_controller(ended_normally=False,
                                                 deactivate_timer=True)
                            self.logger.info(
                                "Method has ended. "
                                "Activate the Timer controller to start it again."
                            )
                        else:
                            this_controller = db_retrieve_table_daemon(
                                Timer, device_id=self.timer_id)
                            setpoint, ended = calculate_method_setpoint(
                                self.method_id, Timer, this_controller, Method,
                                MethodData, self.logger)
                            if ended:
                                self.method_start_act = 'Ended'
                            if setpoint > 100:
                                setpoint = 100
                            elif setpoint < 0:
                                setpoint = 0
                            self.logger.debug(
                                "Turn Output {output} to a PWM duty cycle of "
                                "{dc:.1f} %".format(output=self.output_id,
                                                    dc=setpoint))
                            # Activate pwm with calculated duty cycle
                            self.control.relay_on(self.output_id,
                                                  duty_cycle=setpoint)
                        self.pwm_method_timer = time.time(
                        ) + self.method_period
                except Exception:
                    self.logger.exception(1)

            time.sleep(0.1)

        self.control.relay_off(self.output_id)
        self.running = False
        self.logger.info("Deactivated in {:.1f} ms".format(
            (timeit.default_timer() - self.thread_shutdown_timer) * 1000))
Beispiel #30
0
    def calculate_method_setpoint(self, method_id):
        method = db_retrieve_table_daemon(Method)

        method_key = method.filter(Method.id == method_id).first()

        method_data = db_retrieve_table_daemon(MethodData)
        method_data = method_data.filter(MethodData.method_id == method_id)

        method_data_all = method_data.filter(MethodData.relay_id == None).all()
        method_data_first = method_data.filter(
            MethodData.relay_id == None).first()

        now = datetime.datetime.now()

        # Calculate where the current time/date is within the time/date method
        if method_key.method_type == 'Date':
            for each_method in method_data_all:
                start_time = datetime.datetime.strptime(
                    each_method.time_start, '%Y-%m-%d %H:%M:%S')
                end_time = datetime.datetime.strptime(each_method.time_end,
                                                      '%Y-%m-%d %H:%M:%S')
                if start_time < now < end_time:
                    setpoint_start = each_method.setpoint_start
                    if each_method.setpoint_end:
                        setpoint_end = each_method.setpoint_end
                    else:
                        setpoint_end = each_method.setpoint_start

                    setpoint_diff = abs(setpoint_end - setpoint_start)
                    total_seconds = (end_time - start_time).total_seconds()
                    part_seconds = (now - start_time).total_seconds()
                    percent_total = part_seconds / total_seconds

                    if setpoint_start < setpoint_end:
                        new_setpoint = setpoint_start + (setpoint_diff *
                                                         percent_total)
                    else:
                        new_setpoint = setpoint_start - (setpoint_diff *
                                                         percent_total)

                    self.logger.debug("[Method] Start: {} End: {}".format(
                        start_time, end_time))
                    self.logger.debug("[Method] Start: {} End: {}".format(
                        setpoint_start, setpoint_end))
                    self.logger.debug(
                        "[Method] Total: {} Part total: {} ({}%)".format(
                            total_seconds, part_seconds, percent_total))
                    self.logger.debug(
                        "[Method] New Setpoint: {}".format(new_setpoint))
                    self.set_point = new_setpoint
                    return 0

        # Calculate where the current Hour:Minute:Seconds is within the Daily method
        elif method_key.method_type == 'Daily':
            daily_now = datetime.datetime.now().strftime('%H:%M:%S')
            daily_now = datetime.datetime.strptime(str(daily_now), '%H:%M:%S')
            for each_method in method_data_all:
                start_time = datetime.datetime.strptime(
                    each_method.time_start, '%H:%M:%S')
                end_time = datetime.datetime.strptime(each_method.time_end,
                                                      '%H:%M:%S')
                if start_time < daily_now < end_time:
                    setpoint_start = each_method.setpoint_start
                    if each_method.setpoint_end:
                        setpoint_end = each_method.setpoint_end
                    else:
                        setpoint_end = each_method.setpoint_start

                    setpoint_diff = abs(setpoint_end - setpoint_start)
                    total_seconds = (end_time - start_time).total_seconds()
                    part_seconds = (daily_now - start_time).total_seconds()
                    percent_total = part_seconds / total_seconds

                    if setpoint_start < setpoint_end:
                        new_setpoint = setpoint_start + (setpoint_diff *
                                                         percent_total)
                    else:
                        new_setpoint = setpoint_start - (setpoint_diff *
                                                         percent_total)

                    self.logger.debug("[Method] Start: {} End: {}".format(
                        start_time.strftime('%H:%M:%S'),
                        end_time.strftime('%H:%M:%S')))
                    self.logger.debug("[Method] Start: {} End: {}".format(
                        setpoint_start, setpoint_end))
                    self.logger.debug(
                        "[Method] Total: {} Part total: {} ({}%)".format(
                            total_seconds, part_seconds, percent_total))
                    self.logger.debug(
                        "[Method] New Setpoint: {}".format(new_setpoint))
                    self.set_point = new_setpoint
                    return 0

        # Calculate sine y-axis value from the x-axis (seconds of the day)
        elif method_key.method_type == 'DailySine':
            new_setpoint = sine_wave_y_out(method_data_first.amplitude,
                                           method_data_first.frequency,
                                           method_data_first.shift_angle,
                                           method_data_first.shift_y)
            self.set_point = new_setpoint
            return 0

        # Calculate Bezier curve y-axis value from the x-axis (seconds of the day)
        elif method_key.method_type == 'DailyBezier':
            new_setpoint = bezier_curve_y_out(
                method_data_first.shift_angle,
                (method_data_first.x0, method_data_first.y0),
                (method_data_first.x1, method_data_first.y1),
                (method_data_first.x2, method_data_first.y2),
                (method_data_first.x3, method_data_first.y3))
            self.set_point = new_setpoint
            return 0

        # Calculate the duration in the method based on self.method_start_time
        elif method_key.method_type == 'Duration' and self.method_start_time != 'Ended':
            seconds_from_start = (now - self.method_start_time).total_seconds()
            total_sec = 0
            previous_total_sec = 0
            for each_method in method_data_all:
                total_sec += each_method.duration_sec
                if previous_total_sec <= seconds_from_start < total_sec:
                    row_start_time = float(
                        self.method_start_time.strftime(
                            '%s')) + previous_total_sec
                    row_since_start_sec = (
                        now - (self.method_start_time + datetime.timedelta(
                            0, previous_total_sec))).total_seconds()
                    percent_row = row_since_start_sec / each_method.duration_sec

                    setpoint_start = each_method.setpoint_start
                    if each_method.setpoint_end:
                        setpoint_end = each_method.setpoint_end
                    else:
                        setpoint_end = each_method.setpoint_start
                    setpoint_diff = abs(setpoint_end - setpoint_start)
                    if setpoint_start < setpoint_end:
                        new_setpoint = setpoint_start + (setpoint_diff *
                                                         percent_row)
                    else:
                        new_setpoint = setpoint_start - (setpoint_diff *
                                                         percent_row)

                    self.logger.debug(
                        "[Method] Start: {} Seconds Since: {}".format(
                            self.method_start_time, seconds_from_start))
                    self.logger.debug("[Method] Start time of row: {}".format(
                        datetime.datetime.fromtimestamp(row_start_time)))
                    self.logger.debug(
                        "[Method] Sec since start of row: {}".format(
                            row_since_start_sec))
                    self.logger.debug(
                        "[Method] Percent of row: {}".format(percent_row))
                    self.logger.debug(
                        "[Method] New Setpoint: {}".format(new_setpoint))
                    self.set_point = new_setpoint
                    return 0
                previous_total_sec = total_sec

            # Duration method has ended, reset method_start_time locally and in DB
            if self.method_start_time:
                with session_scope(MYCODO_DB_PATH) as db_session:
                    mod_method = db_session.query(Method).filter(
                        Method.id == self.method_id).first()
                    mod_method.method_start_time = 'Ended'
                    db_session.commit()
                self.method_start_time = 'Ended'

        # Setpoint not needing to be calculated, use default setpoint
        self.set_point = self.default_set_point