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))
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))