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