示例#1
0
    def check_triggers(self):
        """
        Check if any Triggers are activated and
        execute their actions if so.

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

        "if measured temperature is above 30C" is the Trigger to check.
        "notify [email protected]" is the Trigger Action to execute if the
        Trigger is True.
        """
        now = time.time()
        timestamp = datetime.datetime.fromtimestamp(now).strftime(
            '%Y-%m-%d %H:%M:%S')
        message = "{ts}\n[Trigger {id} ({name})]".format(
            ts=timestamp,
            name=self.trigger_name,
            id=self.unique_id)

        trigger = db_retrieve_table_daemon(
            Trigger, unique_id=self.unique_id, entry='first')

        device_id = trigger.measurement.split(',')[0]

        # if len(trigger.measurement.split(',')) > 1:
        #     device_measurement = trigger.measurement.split(',')[1]
        # else:
        #     device_measurement = None

        device = None

        input_dev = db_retrieve_table_daemon(
            Input, unique_id=device_id, entry='first')
        if input_dev:
            device = input_dev

        math = db_retrieve_table_daemon(
            Math, unique_id=device_id, entry='first')
        if math:
            device = math

        output = db_retrieve_table_daemon(
            Output, unique_id=device_id, entry='first')
        if output:
            device = output

        pid = db_retrieve_table_daemon(
            PID, unique_id=device_id, entry='first')
        if pid:
            device = pid

        if not device:
            message += " Error: Controller not Input, Math, Output, or PID"
            self.logger.error(message)
            return

        # If the edge detection variable is set, calling this function will
        # trigger an edge detection event. This will merely produce the correct
        # message based on the edge detection settings.
        elif trigger.trigger_type == 'trigger_edge':
            try:
                GPIO.setmode(GPIO.BCM)
                GPIO.setup(int(input_dev.pin), GPIO.IN)
                gpio_state = GPIO.input(int(input_dev.pin))
            except:
                gpio_state = None
                self.logger.error("Exception reading the GPIO pin")
            if (gpio_state is not None and
                    gpio_state == trigger.if_sensor_gpio_state):
                message += " GPIO State Detected (state = {state}).".format(
                    state=trigger.if_sensor_gpio_state)
            else:
                self.logger.error("GPIO not configured correctly or GPIO state not verified")
                return

        # Calculate the sunrise/sunset times and find the next time this trigger should trigger
        elif trigger.trigger_type == 'trigger_sunrise_sunset':
            # Since the check time is the trigger time, we will only calculate and set the next trigger time
            self.timer_period = calculate_sunrise_sunset_epoch(trigger)

        # Check if the current time is between the start and end time
        elif trigger.trigger_type == 'trigger_timer_daily_time_span':
            if not time_between_range(self.timer_start_time,
                                      self.timer_end_time):
                return

        # If the code hasn't returned by now, action should be executed
        trigger_function_actions(
            self.unique_id,
            message=message,
            debug=self.log_level_debug)
示例#2
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))
示例#3
0
    def check_triggers(self):
        """
        Check if any Triggers are activated and
        execute their actions if so.

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

        "if measured temperature is above 30C" is the Trigger to check.
        "notify [email protected]" is the Trigger Action to execute if the
        Trigger is True.
        """
        last_measurement = None
        gpio_state = None

        logger_cond = logging.getLogger("mycodo.conditional_{id}".format(
            id=self.function_id))

        trigger = db_retrieve_table_daemon(
            Trigger, unique_id=self.function_id, entry='first')

        now = time.time()
        timestamp = datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H:%M:%S')
        message = "{ts}\n[Trigger {id} ({name})]".format(
            ts=timestamp,
            name=trigger.name,
            id=self.function_id)

        device_id = trigger.measurement.split(',')[0]

        if len(trigger.measurement.split(',')) > 1:
            device_measurement = trigger.measurement.split(',')[1]
        else:
            device_measurement = None

        device = None

        input_dev = db_retrieve_table_daemon(
            Input, unique_id=device_id, entry='first')
        if input_dev:
            device = input_dev

        math = db_retrieve_table_daemon(
            Math, unique_id=device_id, entry='first')
        if math:
            device = math

        output = db_retrieve_table_daemon(
            Output, unique_id=device_id, entry='first')
        if output:
            device = output

        pid = db_retrieve_table_daemon(
            PID, unique_id=device_id, entry='first')
        if pid:
            device = pid

        if not device:
            message += " Error: Controller not Input, Math, Output, or PID"
            logger_cond.error(message)
            return

        # If the edge detection variable is set, calling this function will
        # trigger an edge detection event. This will merely produce the correct
        # message based on the edge detection settings.
        elif trigger.trigger_type == 'trigger_edge':
            try:
                GPIO.setmode(GPIO.BCM)
                GPIO.setup(int(input_dev.pin), GPIO.IN)
                gpio_state = GPIO.input(int(input_dev.pin))
            except:
                gpio_state = None
                logger_cond.error("Exception reading the GPIO pin")
            if (gpio_state is not None and
                    gpio_state == trigger.if_sensor_gpio_state):
                message += " GPIO State Detected (state = {state}).".format(
                    state=trigger.if_sensor_gpio_state)
            else:
                logger_cond.error("GPIO not configured correctly or GPIO state not verified")
                return

        # Calculate the sunrise/sunset times and find the next time this trigger should trigger
        elif trigger.trigger_type == 'trigger_sunrise_sunset':
            # Since the check time is the trigger time, we will only calculate and set the next trigger time
            self.timer_period = calculate_sunrise_sunset_epoch(trigger)

        # Check if the current time is between the start and end time
        if trigger.trigger_type == 'trigger_timer_daily_time_span':
            if not time_between_range(self.timer_start_time, self.timer_end_time):
                return

        # If the code hasn't returned by now, action should be executed
        trigger_function_actions(self.function_id, message=message)
示例#4
0
    def check_triggers(self):
        """
        Check if any Triggers are activated and
        execute their actions if so.

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

        "if measured temperature is above 30C" is the Trigger to check.
        "notify [email protected]" is the Trigger Action to execute if the
        Trigger is True.
        """
        now = time.time()
        timestamp = datetime.datetime.fromtimestamp(now).strftime(
            '%Y-%m-%d %H:%M:%S')
        message = f"{timestamp}\n[Trigger {self.unique_id} ({self.trigger_name})]"

        trigger = db_retrieve_table_daemon(Trigger,
                                           unique_id=self.unique_id,
                                           entry='first')

        device_id = trigger.measurement.split(',')[0]

        device = None

        input_dev = db_retrieve_table_daemon(Input,
                                             unique_id=device_id,
                                             entry='first')
        if input_dev:
            device = input_dev

        function = db_retrieve_table_daemon(CustomController,
                                            unique_id=device_id,
                                            entry='first')
        if function:
            device = CustomController

        output = db_retrieve_table_daemon(Output,
                                          unique_id=device_id,
                                          entry='first')
        if output:
            device = output

        pid = db_retrieve_table_daemon(PID, unique_id=device_id, entry='first')
        if pid:
            device = pid

        if not device:
            message += " Error: Controller not Input, Function, Output, or PID"
            self.logger.error(message)
            return

        # If the edge detection variable is set, calling this function will
        # trigger an edge detection event. This will merely produce the correct
        # message based on the edge detection settings.
        elif trigger.trigger_type == 'trigger_edge':
            try:
                import RPi.GPIO as GPIO
                GPIO.setmode(GPIO.BCM)
                GPIO.setup(int(input_dev.pin), GPIO.IN)
                gpio_state = GPIO.input(int(input_dev.pin))
            except Exception as e:
                gpio_state = None
                self.logger.error(f"Exception reading the GPIO pin: {e}")
            if (gpio_state is not None
                    and gpio_state == trigger.if_sensor_gpio_state):
                message += f" GPIO State Detected (state = {trigger.if_sensor_gpio_state})."
            else:
                self.logger.error(
                    "GPIO not configured correctly or GPIO state not verified")
                return

        # Calculate the sunrise/sunset times and find the next time this trigger should trigger
        elif trigger.trigger_type == 'trigger_sunrise_sunset':
            # Since the check time is the trigger time, we will only calculate and set the next trigger time
            self.timer_period = suntime_calculate_next_sunrise_sunset_epoch(
                trigger.latitude, trigger.longitude, trigger.date_offset_days,
                trigger.time_offset_minutes, trigger.rise_or_set)

        # Check if the current time is between the start and end time
        elif trigger.trigger_type == 'trigger_timer_daily_time_span':
            if not time_between_range(self.timer_start_time,
                                      self.timer_end_time):
                return

        # If the code hasn't returned by now, action should be executed
        actions = parse_action_information()
        trigger_controller_actions(actions,
                                   self.unique_id,
                                   message=message,
                                   debug=self.log_level_debug)
示例#5
0
    def check_conditionals(self):
        """
        Check if any Conditionals are activated and
        execute their actions if the Conditional is true.

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

        "if measured temperature is above 30C" is the Conditional to check.
        "notify [email protected]" is the Conditional Action to execute if the
        Conditional is True.
        """
        last_measurement = None
        gpio_state = None

        logger_cond = logging.getLogger(
            "mycodo.conditional_{id}".format(id=self.cond_id))

        cond = db_retrieve_table_daemon(Conditional,
                                        unique_id=self.cond_id,
                                        entry='first')

        now = time.time()
        timestamp = datetime.datetime.fromtimestamp(now).strftime(
            '%Y-%m-%d %H:%M:%S')
        message = "{ts}\n[Conditional {id} ({name})]".format(ts=timestamp,
                                                             name=cond.name,
                                                             id=self.cond_id)

        device_id = cond.measurement.split(',')[0]

        if len(cond.measurement.split(',')) > 1:
            device_measurement = cond.measurement.split(',')[1]
        else:
            device_measurement = None

        direction = cond.direction
        setpoint = cond.setpoint
        max_age = cond.max_age

        device = None

        input_dev = db_retrieve_table_daemon(Input,
                                             unique_id=device_id,
                                             entry='first')
        if input_dev:
            device = input_dev

        math = db_retrieve_table_daemon(Math,
                                        unique_id=device_id,
                                        entry='first')
        if math:
            device = math

        output = db_retrieve_table_daemon(Output,
                                          unique_id=device_id,
                                          entry='first')
        if output:
            device = output

        pid = db_retrieve_table_daemon(PID, unique_id=device_id, entry='first')
        if pid:
            device = pid

        if not device:
            message += " Error: Controller not Input, Math, Output, or PID"
            logger_cond.error(message)
            return

        # Check Measurement Conditionals
        if (cond.conditional_type == 'conditional_measurement' and direction
                and device_id and device_measurement):

            # Check if there hasn't been a measurement in the last set number
            # of seconds. If not, trigger conditional
            if direction == 'none_found':
                last_measurement = self.get_last_measurement(
                    device_id, device_measurement, max_age)
                if last_measurement is None:
                    message += " Measurement {meas} for device ID {id} not found in the past" \
                               " {value} seconds.".format(
                        meas=device_measurement,
                        id=device_id,
                        value=max_age)
                else:
                    return

            # Check if last measurement is greater or less than the set value
            else:
                last_measurement = self.get_last_measurement(
                    device_id, device_measurement, max_age)
                if last_measurement is None:
                    logger_cond.debug("Last measurement not found")
                    return
                elif ((direction == 'above' and last_measurement > setpoint) or
                      (direction == 'below' and last_measurement < setpoint)):

                    message += " Measurement {meas}: {value} ".format(
                        meas=device_measurement, value=last_measurement)
                    if direction == 'above':
                        message += ">"
                    elif direction == 'below':
                        message += "<"
                    message += " {sp} (set value).".format(sp=setpoint)
                else:
                    return  # Not triggered

        # If the edge detection variable is set, calling this function will
        # trigger an edge detection event. This will merely produce the correct
        # message based on the edge detection settings.
        elif cond.conditional_type == 'conditional_edge':
            try:
                GPIO.setmode(GPIO.BCM)
                GPIO.setup(int(input_dev.pin), GPIO.IN)
                gpio_state = GPIO.input(int(input_dev.pin))
            except:
                gpio_state = None
                logger_cond.error("Exception reading the GPIO pin")
            if (input_dev and input_dev.location and gpio_state is not None
                    and gpio_state == cond.if_sensor_gpio_state):
                message += " GPIO State Detected (state = {state}).".format(
                    state=cond.if_sensor_gpio_state)
            else:
                logger_cond.error(
                    "GPIO not configured correctly or GPIO state not verified")
                return

        # Calculate the sunrise/sunset times and find the next time this conditional should trigger
        elif cond.conditional_type == 'conditional_sunrise_sunset':
            # Since the check time is the trigger time, we will only calculate and set the next trigger time
            self.timer_period = self.calculate_sunrise_sunset_epoch(cond)

        # Set the refractory period
        if cond.conditional_type == 'conditional_measurement':
            self.timer_refractory_period = time.time() + self.refractory_period

        # Check if the current time is between the start and end time
        if cond.conditional_type == 'conditional_timer_daily_time_span':
            if not time_between_range(self.timer_start_time,
                                      self.timer_end_time):
                return

        # If the code hasn't returned by now, the conditional has been triggered
        # and the actions for that conditional should be executed
        self.trigger_conditional_actions(message=message,
                                         last_measurement=last_measurement,
                                         device_id=device_id,
                                         device_measurement=device_measurement,
                                         edge=gpio_state)