Beispiel #1
0
    def checkConditionals(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 each_cond: Object of SQL table entries for a specific column
        :type each_cond: sqlalchemy object
        """
        attachment_file = False
        attachment_type = False

        last_measurement = self.getLastMeasurement(self.cond_measurement_type[cond_id])
        if (last_measurement and
                ((self.cond_direction[cond_id] == 'above' and
                    last_measurement > self.cond_setpoint[cond_id]) or
                (self.cond_direction[cond_id] == 'below' and
                    last_measurement < self.cond_setpoint[cond_id]))):

            now = time.time()
            timestamp = datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H-%M-%S')
            message = "{}\n[Sensor Conditional {}] {}\n{} {} ".format(
                    timestamp, cond_id,
                    self.cond_name[cond_id],
                    self.cond_measurement_type[cond_id],
                    last_measurement)
            if self.cond_direction[cond_id] == 'above':
                message += ">"
            elif self.cond_direction[cond_id] == 'below':
                message += "<"
            message += " {} setpoint.".format(self.cond_setpoint[cond_id])

            if (self.cond_relay_id[cond_id] and
                    self.cond_relay_state[cond_id] in ['on', 'off']):
                message += "\nTurning relay {} {}".format(
                        self.cond_relay_id[cond_id],
                        self.cond_relay_state[cond_id])
                if (self.cond_relay_state[cond_id] == 'on' and
                        self.cond_relay_on_duration[cond_id]):
                    message += " for {} seconds".format(self.cond_relay_on_duration[cond_id])
                message += ". "
                relay_on_off = threading.Thread(
                    target=self.control.relay_on_off,
                    args=(self.cond_relay_id[cond_id],
                          self.cond_relay_state[cond_id],
                          self.cond_relay_on_duration[cond_id],))
                relay_on_off.start()

            # Execute command in shell
            if self.cond_execute_command[cond_id]:
                message += "\nExecute '{}'. ".format(
                        self.cond_execute_command[cond_id])
                cmd_out, cmd_err, cmd_status = cmd_output(self.cond_execute_command[cond_id])
                message += "Status: {}. ".format(cmd_status)

            if self.cond_camera_record[cond_id] in ['photo', 'photoemail']:
                attachment_file = camera_record('photo')
            elif self.cond_camera_record[cond_id] in ['video', 'videoemail']:
                attachment_file = camera_record('video', 5)

            if self.cond_email_notify[cond_id]:
                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 += "\nNotify {}.".format(
                            self.cond_email_notify[cond_id])
                    # attachment_type != False indicates to
                    # attach a photo or video
                    if self.cond_camera_record[cond_id] == 'photoemail':
                        message += "\nPhoto attached."
                        attachment_type = 'still'
                    elif self.cond_camera_record[cond_id] == 'videoemail':
                        message += "\nVideo attached."
                        attachment_type = 'video'
                    with session_scope(MYCODO_DB_PATH) as new_session:
                        smtp = new_session.query(SMTP).first()
                        email(self.logger, smtp.host, smtp.ssl, smtp.port,
                              smtp.user, smtp.passw, smtp.email_from,
                              self.cond_email_notify[cond_id], message,
                              attachment_file, attachment_type)
                else:
                    self.logger.debug("[Sensor Conditional {}] "
                                      "{:.0f} seconds left to be "
                                      "allowed to email again.".format(
                                      cond_id,
                                      (self.smtp_wait_timer[cond_id]-time.time())))

            if self.cond_flash_lcd[cond_id]:
                start_flashing = threading.Thread(
                    target=self.control.flash_lcd,
                    args=(self.cond_flash_lcd[cond_id],
                          1,))
                start_flashing.start()

            self.logger.debug(message)
        else:
            self.logger.debug("[Sensor Conditional {}] Last measurement "
                              "not found".format(cond_id))
    def checkConditionals(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 each_cond: Object of SQL table entries for a specific column
        :type each_cond: sqlalchemy object
        """
        last_measurement = self.getLastMeasurement(self.cond_measurement_type[cond_id])
        if last_measurement:
            message = "[Sensor Conditional {}] Last measurement {} is ".format(
                    cond_id, last_measurement)

            if ((self.cond_direction[cond_id] == 'above' and
                    last_measurement > self.cond_setpoint[cond_id]) or
                (self.cond_direction[cond_id] == 'below' and
                    last_measurement < self.cond_setpoint[cond_id])):

                if (self.cond_relay_id[cond_id] and
                        self.cond_relay_state[cond_id] in ['on', 'off']):
                    message += " {} {}, turning relay {} {}".format(
                            self.cond_direction[cond_id],
                            self.cond_setpoint[cond_id],
                            self.cond_relay_id[cond_id],
                            self.cond_relay_state[cond_id])
                    if (self.cond_relay_state[cond_id] == 'on' and
                            self.cond_relay_on_duration[cond_id]):
                        message += " for {} seconds".format(self.cond_relay_on_duration[cond_id])
                    message += ". "
                    relay_on_off = threading.Thread(
                        target=self.control.relay_on_off,
                        args=(self.cond_relay_id[cond_id],
                              self.cond_relay_state[cond_id],
                              self.cond_relay_on_duration[cond_id],))
                    relay_on_off.start()

                if self.cond_execute_command[cond_id]:
                    message += "Execute '{}'. ".format(
                            self.cond_execute_command[cond_id])
                    # execute command here

                if self.cond_email_notify[cond_id]:
                    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 self.allowed_to_send_notice:
                        message += "Notify {}.".format(
                                self.cond_email_notify[cond_id])
                        with session_scope(MYCODO_DB_PATH) as new_session:
                            smtp = new_session.query(SMTP).first()
                            email(self.logger, smtp.host, smtp.ssl, smtp.port,
                                  smtp.user, smtp.passw, smtp.email_from,
                                  self.cond_email_notify[cond_id], message)
                    else:
                        self.logger.debug("[Sensor Conditional {}] "
                                          "{:.0f} seconds left to be "
                                          "allowed to email again.".format(
                                          cond_id,
                                          (self.smtp_wait_timer[cond_id]-time.time())))

                if self.cond_flash_lcd[cond_id]:
                    start_flashing = threading.Thread(
                        target=self.control.flash_lcd,
                        args=(self.cond_flash_lcd[cond_id],
                              1,))
                    start_flashing.start()

            self.logger.debug(message)
        else:
            self.logger.debug("[Sensor Conditional {}] Last measurement "
                              "not found".format(cond_id))
    def relay_on_off(self, relay_id, state,
                     duration=0.0, trigger_conditionals=True):
        """
        Turn a relay on or off
        The GPIO may be either HIGH or LOW to activate a relay. This trigger
        state will be referenced to determine if the GPIO needs to be high or
        low to turn the relay on or off.

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

        :param relay_id: Unique ID for relay
        :type relay_id: str
        :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 relay off after
        :type duration: float
        :param trigger_conditionals: Whether to trigger condionals to act or not
        :type trigger_conditionals: bool
        """
        # Check if relay exists
        if relay_id not in self.relay_id:
            self.logger.warning("[Relay] Cannot turn {} Relay with ID {}. It "
                                "doesn't exist".format(state, relay_id))
            return 1
        if state == 'on':
            if not self.relay_pin[relay_id]:
                self.logger.warning("[Relay] Cannot turn a relay "
                                    "{} ({}) on with a pin of "
                                    "0.".format(self.relay_id[relay_id],
                                                self.relay_name[relay_id]))
                return 1

            current_amps = self.current_amp_load()
            if current_amps+self.relay_amps[relay_id] > MAX_AMPS:
                self.logger.warning("[Relay] Cannot turn relay {} "
                                    "({}) On. If this relay turns on, "
                                    "there will be {} amps being drawn, "
                                    "which exceeds the maximum set draw of {}"
                                    " amps.".format(self.relay_id[relay_id],
                                                    self.relay_name[relay_id],
                                                    current_amps,
                                                    MAX_AMPS))
                return 1

            else:
                if duration:
                    time_now = datetime.datetime.now()
                    if self.is_on(relay_id) and self.relay_on_duration[relay_id]:
                        if self.relay_on_until[relay_id] > time_now:
                            remaining_time = (self.relay_on_until[relay_id]-time_now).seconds
                        else:
                            remaining_time = 0
                        time_on = self.relay_last_duration[relay_id] - remaining_time
                        self.logger.debug("[Relay] Relay {} ({}) is already "
                                            "on for a duration of {:.1f} seconds (with "
                                            "{:.1f} seconds remaining). Recording the "
                                            "amount of time the relay has been on ({:.1f} "
                                            "sec) and updating the on duration to {:.1f} "
                                            "seconds.".format(self.relay_id[relay_id],
                                                              self.relay_name[relay_id],
                                                              self.relay_last_duration[relay_id],
                                                              remaining_time,
                                                              time_on,
                                                              duration))
                        if time_on > 0:
                            write_db = threading.Thread(
                                target=write_influxdb,
                                args=(self.logger, INFLUXDB_HOST,
                                      INFLUXDB_PORT, INFLUXDB_USER,
                                      INFLUXDB_PASSWORD, INFLUXDB_DATABASE,
                                      'relay', relay_id, 'duration_sec',
                                      float(time_on),))
                            write_db.start()

                        self.relay_on_until[relay_id] = time_now+datetime.timedelta(seconds=duration)
                        self.relay_last_duration[relay_id] = duration
                        return 0
                    elif self.is_on(relay_id) and not self.relay_on_duration:
                        self.relay_on_duration[relay_id] = True
                        self.relay_on_until[relay_id] = time_now+datetime.timedelta(seconds=duration)
                        self.relay_last_duration[relay_id] = duration

                        self.logger.debug("[Relay] Relay {} ({}) is currently"
                                          " on without a duration. Turning "
                                          "into a duration  of {:.1f} "
                                          "seconds.".format(self.relay_id[relay_id],
                                                            self.relay_name[relay_id],
                                                            duration))
                        return 0
                    else:
                        self.relay_on_until[relay_id] = time_now+datetime.timedelta(seconds=duration)
                        self.relay_on_duration[relay_id] = True
                        self.relay_last_duration[relay_id] = duration
                        self.logger.debug("[Relay] Relay {} ({}) on for {:.1f} "
                                          "seconds.".format(self.relay_id[relay_id],
                                                             self.relay_name[relay_id],
                                                             duration))
                        GPIO.output(self.relay_pin[relay_id], self.relay_trigger[relay_id])

                else:
                    if self.is_on(relay_id):
                        self.logger.warning("[Relay] Relay {} ({}) is already on.".format(
                                self.relay_id[relay_id],
                                self.relay_name[relay_id]))
                        return 1
                    else:
                        GPIO.output(self.relay_pin[relay_id],
                                    self.relay_trigger[relay_id])

        else:
            if self._is_setup() and self.relay_pin[relay_id]:  # if pin not 0
                self.relay_on_duration[relay_id] = False
                self.relay_on_until[relay_id] = datetime.datetime.now()
                GPIO.output(self.relay_pin[relay_id], not self.relay_trigger[relay_id])
                self.logger.debug("[Relay] Relay {} ({}) turned off.".format(
                        self.relay_id[relay_id],
                        self.relay_name[relay_id]))

        if trigger_conditionals:
            with session_scope(MYCODO_DB_PATH) as new_session:
                conditionals = new_session.query(RelayConditional)
                new_session.expunge_all()
                new_session.close()

            conditionals = conditionals.filter(RelayConditional.if_relay_id == relay_id)
            conditionals = conditionals.filter(RelayConditional.activated == True)

            if self.is_on(relay_id):
                conditionals = conditionals.filter(RelayConditional.if_action == 'on')
                conditionals = conditionals.filter(RelayConditional.if_duration == 0)
            else:
                conditionals = conditionals.filter(RelayConditional.if_action == 'off')

            for each_conditional in conditionals.all():
                message = None
                if (each_conditional.do_relay_id or
                        each_conditional.execute_command or
                        each_conditional.email_notify):
                    message = "[Relay Conditional {}] " \
                              "If relay {} ({}) turns " \
                              "{}: ".format(each_conditional.id,
                                            each_conditional.if_relay_id,
                                            self.relay_name[each_conditional.if_relay_id],
                                            each_conditional.if_action)

                if each_conditional.do_relay_id:
                    message += "Turn relay {} ({}) {}".format(
                            each_conditional.do_relay_id,
                            self.relay_name[each_conditional.do_relay_id],
                            each_conditional.do_action)

                    if each_conditional.do_duration == 0:
                        self.relay_on_off(each_conditional.do_relay_id,
                                          each_conditional.do_action)
                    else:
                        message += " for {} seconds".format(each_conditional.do_duration)
                        self.relay_on_off(each_conditional.do_relay_id,
                                          each_conditional.do_action,
                                          each_conditional.do_duration)
                    message += ". "


                if each_conditional.execute_command:
                    #################################
                    #        DANGEROUS CODE         #
                    #################################
                    # This code is not secure at all#
                    # and could cause serious       #
                    # damage to your software and   #
                    # hardware.                     #
                    #################################

                    # TODO: SECURITY: FIX THIS This runs arbitrary as ROOT
                    #       Make sure this works (currently untested)

                    message += "Execute '{}'. ".format(each_conditional.execute_command)
                    pass
                    # p = subprocess.Popen(each_conditional.execute_command, shell=True,
                    #                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                    # output, errors = p.communicate()
                    # self.logger.debug("[Relay Conditional {} ({})] "
                    #                   "Execute command: {} "
                    #                   "Command output: {} "
                    #                   "Command errors: {}".format(each_conditional.id,
                    #                                               each_conditional.name,
                    #                                               each_conditional.execute_command,
                    #                                               output, errors))

                if each_conditional.email_notify:
                    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 += "Notify {}.".format(
                            each_conditional.id,
                            each_conditional.email_notify)

                        with session_scope(MYCODO_DB_PATH) as new_session:
                            smtp = new_session.query(SMTP).first()
                            email(self.logger, smtp.host, smtp.ssl, smtp.port,
                                  smtp.user, smtp.passw, smtp.email_from,
                                  each_conditional.email_notify, message)
                    else:
                        self.logger.debug("[Relay Conditional {}] True: "
                                          "{:.0f} seconds left to be "
                                          "allowed to email again.".format(
                                          each_cond.id,
                                          (self.smtp_wait_time-time.time())))

                if each_conditional.flash_lcd:
                    start_flashing = threading.Thread(
                        target=self.control.flash_lcd,
                        args=(each_conditional.flash_lcd,
                              1,))
                    start_flashing.start()

                if (each_conditional.do_relay_id or
                        each_conditional.execute_command or
                        each_conditional.email_notify):
                    self.logger.debug("{}".format(message))
Beispiel #4
0
    def checkConditionals(self, relay_id, on_duration):
        with session_scope(MYCODO_DB_PATH) as new_session:
            conditionals = new_session.query(RelayConditional)
            new_session.expunge_all()
            new_session.close()

        conditionals = conditionals.filter(RelayConditional.if_relay_id == relay_id)
        conditionals = conditionals.filter(RelayConditional.activated == True)

        if self.is_on(relay_id):
            conditionals = conditionals.filter(RelayConditional.if_action == 'on')
            conditionals = conditionals.filter(RelayConditional.if_duration == on_duration)
        else:
            conditionals = conditionals.filter(RelayConditional.if_action == 'off')

        for each_conditional in conditionals.all():
            message = None
            if (each_conditional.do_relay_id or
                    each_conditional.execute_command or
                    each_conditional.email_notify):
                now = time.time()
                timestamp = datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H-%M-%S')
                message = "{}\n[Relay Conditional {}] {}\n".format(
                    timestamp, each_conditional.id, each_conditional.name)
                message += "If relay {} ({}) turns {}, Then:\n".format(
                    each_conditional.if_relay_id,
                    self.relay_name[each_conditional.if_relay_id],
                    each_conditional.if_action)

            if each_conditional.do_relay_id:
                message += "Turn relay {} ({}) {}".format(
                        each_conditional.do_relay_id,
                        self.relay_name[each_conditional.do_relay_id],
                        each_conditional.do_action)

                if each_conditional.do_duration == 0:
                    self.relay_on_off(each_conditional.do_relay_id,
                                      each_conditional.do_action)
                else:
                    message += " for {} seconds".format(each_conditional.do_duration)
                    self.relay_on_off(each_conditional.do_relay_id,
                                      each_conditional.do_action,
                                      each_conditional.do_duration)
                message += ".\n"

            if each_conditional.execute_command:
                #################################
                #        DANGEROUS CODE         #
                #################################
                # This code is not secure at all#
                # and could cause serious       #
                # damage to your software and   #
                # hardware.                     #
                #################################

                # TODO: SECURITY: FIX THIS This runs arbitrary as ROOT
                #       Make sure this works (currently untested)

                message += "Execute: '{}'. ".format(
                    each_conditional.execute_command)
                cmd_out, cmd_err, cmd_status = cmd_output(
                    self.cond_execute_command[cond_id])
                message += "Status: {}. ".format(cmd_status)

            if each_conditional.email_notify:
                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 += "Notify {}.".format(
                        each_conditional.email_notify)

                    with session_scope(MYCODO_DB_PATH) as new_session:
                        smtp = new_session.query(SMTP).first()
                        email(self.logger, smtp.host, smtp.ssl, smtp.port,
                              smtp.user, smtp.passw, smtp.email_from,
                              each_conditional.email_notify, message)
                else:
                    self.logger.debug("[Relay Conditional {}] True: "
                                      "{:.0f} seconds left to be "
                                      "allowed to email again.".format(
                                      each_cond.id,
                                      (self.smtp_wait_time-time.time())))

            if each_conditional.flash_lcd:
                start_flashing = threading.Thread(
                    target=self.control.flash_lcd,
                    args=(each_conditional.flash_lcd,
                          1,))
                start_flashing.start()

            if (each_conditional.do_relay_id or
                    each_conditional.execute_command or
                    each_conditional.email_notify):
                self.logger.debug("{}".format(message))