def __init__(self, ready, logger, sensor_id): threading.Thread.__init__(self) self.thread_startup_timer = timeit.default_timer() self.thread_shutdown_timer = 0 self.ready = ready self.logger = logger self.lock = {} self.sensor_id = sensor_id self.control = DaemonControl() self.pause_loop = False self.verify_pause_loop = True self.setup_sensor_conditionals() # Obtain database configuration options with session_scope(MYCODO_DB_PATH) as new_session: sensor = new_session.query(Sensor).filter( Sensor.id == self.sensor_id).first() self.location = sensor.location self.device_type = sensor.device self.sensor_type = sensor.device_type self.period = sensor.period self.multiplexer_address_raw = sensor.multiplexer_address self.multiplexer_channel = sensor.multiplexer_channel self.adc_channel = 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 if self.device_type == 'EDGE': if sensor.switch_edge == 'rising': self.switch_edge_gpio = GPIO.RISING elif sensor.switch_edge == 'falling': self.switch_edge_gpio = GPIO.FALLING else: self.switch_edge_gpio = GPIO.BOTH self.switch_edge = sensor.switch_edge self.switch_bouncetime = sensor.switch_bouncetime self.switch_reset_period = sensor.switch_reset_period smtp = new_session.query(SMTP).first() self.smtp_max_count = smtp.hourly_max self.email_count = 0 self.allowed_to_send_notice = True # 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 = new_session.query(Relay).all() # Check if relay ID actually exists for each_relay in relay: if each_relay.id == self.pre_relay_id and self.pre_relay_duration: self.pre_relay_setup = True if self.device_type in ['AM2315', 'BMP'] and self.multiplexer_address_raw: self.multiplexer_address_string = self.multiplexer_address_raw self.multiplexer_address = int(str(self.multiplexer_address_raw), 16) self.multiplexer_lock_file = "/var/lock/mycodo_multiplexer_0x{:02X}.pid".format(self.multiplexer_address) self.multiplexer = TCA9548A(self.multiplexer_address) else: self.multiplexer = None if self.device_type in ['ADS1x15','MCP342x'] and self.location: self.adc_lock_file = "/var/lock/mycodo_adc_0x{:02X}.pid".format(int(str(self.location), 16)) else: self.adc = None self.device_recognized = True # Processes if self.device_type == 'RPiCPULoad': self.measure_sensor = RaspberryPiCPULoad() # Environmental Sensors elif self.device_type == 'RPi': self.measure_sensor = RaspberryPiCPUTemp() elif self.device_type == 'DS18B20': self.measure_sensor = DS18B20(self.location) elif self.device_type == 'DHT11': self.measure_sensor = DHT11(pigpio.pi(), int(self.location)) elif self.device_type in ['DHT22', 'AM2302']: self.measure_sensor = DHT22(pigpio.pi(), int(self.location)) elif self.device_type == 'AM2315': self.measure_sensor = AM2315_read() elif self.device_type == 'K30': self.measure_sensor = K30() elif self.device_type == 'BMP': self.measure_sensor = BMP() elif self.device_type == 'SHT1x_7x': self.measure_sensor = SHT1x_7x_read(self.location, self.sht_clock_pin, self.sht_voltage) elif self.device_type == 'SHT2x': self.measure_sensor = SHT2x_read(int(str(self.location), 16)) elif self.device_type == 'TMP006': self.measure_sensor = TMP006_read(self.location) elif self.device_type == 'TSL2561': self.measure_sensor = TSL2561_read(self.location) # Devices elif self.device_type == 'ADS1x15': self.adc = ADS1x15_read(self.logger, int(str(self.location), 16), self.adc_channel, self.adc_gain) elif self.device_type == 'MCP342x': self.adc = MCP342x_read(self.logger, int(str(self.location), 16), self.adc_channel, self.adc_gain, self.adc_resolution) # Other elif self.device_type in ['EDGE', 'ADS1x15', 'MCP342x']: self.measure_sensor = None else: self.device_recognized = False self.logger.debug("[Sensor {}] Device '{}' not " "recognized:".format(self.sensor_id, self.device_type)) raise Exception("{} is not a valid device type.".format( self.device_type)) self.edge_reset_timer = time.time() self.sensor_timer = time.time() self.running = False self.lastUpdate = None
def __init__(self, ready, logger, sensor_id): threading.Thread.__init__(self) list_devices_i2c = [ 'ADS1x15', 'AM2315', 'ATLAS_PT1000', 'BMP', 'HTU21D', 'MCP342x', 'SHT2x', 'TMP006', 'TSL2561' ] self.thread_startup_timer = timeit.default_timer() self.thread_shutdown_timer = 0 self.ready = ready self.logger = logger self.lock = {} self.sensor_id = sensor_id self.control = DaemonControl() self.pause_loop = False self.verify_pause_loop = True self.setup_sensor_conditionals() with session_scope(MYCODO_DB_PATH) as new_session: sensor = new_session.query(Sensor) sensor = sensor.filter(Sensor.id == self.sensor_id).first() self.i2c_bus = sensor.i2c_bus self.location = sensor.location self.device_type = sensor.device self.sensor_type = sensor.device_type self.period = sensor.period self.multiplexer_address_raw = sensor.multiplexer_address self.multiplexer_bus = sensor.multiplexer_bus self.multiplexer_channel = sensor.multiplexer_channel self.adc_channel = 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 = new_session.query(Relay).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 = new_session.query(SMTP).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_type in list_devices_i2c: self.i2c_address = int(str(self.location), 16) # Set up multiplexer if enabled if self.device_type in list_devices_i2c and self.multiplexer_address_raw: self.multiplexer_address_string = self.multiplexer_address_raw self.multiplexer_address = int(str(self.multiplexer_address_raw), 16) self.multiplexer_lock_file = "/var/lock/mycodo_multiplexer_0x{:02X}.pid".format( self.multiplexer_address) self.multiplexer = TCA9548A(self.multiplexer_bus, self.multiplexer_address) else: self.multiplexer = None if self.device_type in ['ADS1x15', 'MCP342x'] and self.location: self.adc_lock_file = "/var/lock/mycodo_adc_bus{}_0x{:02X}.pid".format( self.i2c_bus, self.i2c_address) # Set up edge detection of a GPIO pin if self.device_type == '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_type == 'ADS1x15': self.adc = ADS1x15_read(self.i2c_address, self.i2c_bus, self.adc_channel, self.adc_gain) elif self.device_type == 'MCP342x': self.adc = MCP342x_read(self.i2c_address, self.i2c_bus, self.adc_channel, self.adc_gain, self.adc_resolution) else: self.adc = None self.device_recognized = True # Set up sensor if self.device_type in ['EDGE', 'ADS1x15', 'MCP342x']: self.measure_sensor = None elif self.device_type == 'RPiCPULoad': self.measure_sensor = RaspberryPiCPULoad() elif self.device_type == 'RPi': self.measure_sensor = RaspberryPiCPUTemp() elif self.device_type == 'DS18B20': self.measure_sensor = DS18B20(self.location) elif self.device_type == 'DHT11': self.measure_sensor = DHT11(pigpio.pi(), int(self.location)) elif self.device_type in ['DHT22', 'AM2302']: self.measure_sensor = DHT22(pigpio.pi(), int(self.location)) elif self.device_type == 'HTU21D': self.measure_sensor = HTU21D_read(self.i2c_bus) elif self.device_type == 'AM2315': self.measure_sensor = AM2315_read(self.i2c_bus) elif self.device_type == 'ATLAS_PT1000': self.measure_sensor = Atlas_PT1000(self.i2c_address, self.i2c_bus) elif self.device_type == 'K30': self.measure_sensor = K30() elif self.device_type == 'BMP': self.measure_sensor = BMP(self.i2c_bus) elif self.device_type == 'SHT1x_7x': self.measure_sensor = SHT1x_7x_read(self.location, self.sht_clock_pin, self.sht_voltage) elif self.device_type == 'SHT2x': self.measure_sensor = SHT2x_read(self.i2c_address, self.i2c_bus) elif self.device_type == 'TMP006': self.measure_sensor = TMP006_read(self.i2c_address, self.i2c_bus) elif self.device_type == 'TSL2561': self.measure_sensor = TSL2561_read(self.i2c_address, self.i2c_bus) else: self.device_recognized = False self.logger.debug("[Sensor {}] Device '{}' not " "recognized:".format(self.sensor_id, self.device_type)) raise Exception("{} is not a valid device type.".format( self.device_type)) self.edge_reset_timer = time.time() self.sensor_timer = time.time() self.running = False self.lastUpdate = None
class SensorController(threading.Thread): """ class for controlling the sensor """ def __init__(self, ready, logger, sensor_id): threading.Thread.__init__(self) self.thread_startup_timer = timeit.default_timer() self.thread_shutdown_timer = 0 self.ready = ready self.logger = logger self.lock = {} self.sensor_id = sensor_id self.control = DaemonControl() self.pause_loop = False self.verify_pause_loop = True self.setup_sensor_conditionals() # Obtain database configuration options with session_scope(MYCODO_DB_PATH) as new_session: sensor = new_session.query(Sensor).filter( Sensor.id == self.sensor_id).first() self.location = sensor.location self.device_type = sensor.device self.sensor_type = sensor.device_type self.period = sensor.period self.multiplexer_address_raw = sensor.multiplexer_address self.multiplexer_channel = sensor.multiplexer_channel self.adc_channel = 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 if self.device_type == 'EDGE': if sensor.switch_edge == 'rising': self.switch_edge_gpio = GPIO.RISING elif sensor.switch_edge == 'falling': self.switch_edge_gpio = GPIO.FALLING else: self.switch_edge_gpio = GPIO.BOTH self.switch_edge = sensor.switch_edge self.switch_bouncetime = sensor.switch_bouncetime self.switch_reset_period = sensor.switch_reset_period smtp = new_session.query(SMTP).first() self.smtp_max_count = smtp.hourly_max self.email_count = 0 self.allowed_to_send_notice = True # 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 = new_session.query(Relay).all() # Check if relay ID actually exists for each_relay in relay: if each_relay.id == self.pre_relay_id and self.pre_relay_duration: self.pre_relay_setup = True if self.device_type in ['AM2315', 'BMP'] and self.multiplexer_address_raw: self.multiplexer_address_string = self.multiplexer_address_raw self.multiplexer_address = int(str(self.multiplexer_address_raw), 16) self.multiplexer_lock_file = "/var/lock/mycodo_multiplexer_0x{:02X}.pid".format(self.multiplexer_address) self.multiplexer = TCA9548A(self.multiplexer_address) else: self.multiplexer = None if self.device_type in ['ADS1x15','MCP342x'] and self.location: self.adc_lock_file = "/var/lock/mycodo_adc_0x{:02X}.pid".format(int(str(self.location), 16)) else: self.adc = None self.device_recognized = True # Processes if self.device_type == 'RPiCPULoad': self.measure_sensor = RaspberryPiCPULoad() # Environmental Sensors elif self.device_type == 'RPi': self.measure_sensor = RaspberryPiCPUTemp() elif self.device_type == 'DS18B20': self.measure_sensor = DS18B20(self.location) elif self.device_type == 'DHT11': self.measure_sensor = DHT11(pigpio.pi(), int(self.location)) elif self.device_type in ['DHT22', 'AM2302']: self.measure_sensor = DHT22(pigpio.pi(), int(self.location)) elif self.device_type == 'AM2315': self.measure_sensor = AM2315_read() elif self.device_type == 'K30': self.measure_sensor = K30() elif self.device_type == 'BMP': self.measure_sensor = BMP() elif self.device_type == 'SHT1x_7x': self.measure_sensor = SHT1x_7x_read(self.location, self.sht_clock_pin, self.sht_voltage) elif self.device_type == 'SHT2x': self.measure_sensor = SHT2x_read(int(str(self.location), 16)) elif self.device_type == 'TMP006': self.measure_sensor = TMP006_read(self.location) elif self.device_type == 'TSL2561': self.measure_sensor = TSL2561_read(self.location) # Devices elif self.device_type == 'ADS1x15': self.adc = ADS1x15_read(self.logger, int(str(self.location), 16), self.adc_channel, self.adc_gain) elif self.device_type == 'MCP342x': self.adc = MCP342x_read(self.logger, int(str(self.location), 16), self.adc_channel, self.adc_gain, self.adc_resolution) # Other elif self.device_type in ['EDGE', 'ADS1x15', 'MCP342x']: self.measure_sensor = None else: self.device_recognized = False self.logger.debug("[Sensor {}] Device '{}' not " "recognized:".format(self.sensor_id, self.device_type)) raise Exception("{} is not a valid device type.".format( self.device_type)) self.edge_reset_timer = time.time() self.sensor_timer = time.time() self.running = False self.lastUpdate = None def run(self): try: self.running = True self.logger.info("[Sensor {}] Activated in {}ms".format( self.sensor_id, (timeit.default_timer()-self.thread_startup_timer)*1000)) self.ready.set() # Set up edge detection if self.device_type == 'EDGE': GPIO.setmode(GPIO.BCM) GPIO.setup(int(self.location), GPIO.IN) GPIO.add_event_detect(int(self.location), self.switch_edge_gpio, callback=self.edge_detected, bouncetime=self.switch_bouncetime) while self.running: # Pause loop to modify conditional statements. # Prevents execution of conditional while varibles are # being modified. if self.pause_loop: self.verify_pause_loop = True while self.pause_loop: time.sleep(0.1) if self.device_type in ['EDGE']: # Sensors that are triggered (simple switch, # PIR motion, reed, hall, etc.) pass else: # Sensors that are read at a regular period # Signal that a measurement needs to be obtained if time.time() > self.next_measurement and not self.get_new_measurement: self.get_new_measurement = True self.next_measurement = time.time()+self.period # if signaled and a pre relay is set up correctly, turn the # relay on for the set duration if (self.get_new_measurement and self.pre_relay_setup and not self.pre_relay_activated): relay_on = threading.Thread( target=self.control.relay_on, args=(self.pre_relay_id, self.pre_relay_duration,)) relay_on.start() self.pre_relay_activated = True self.pre_relay_timer = time.time()+self.pre_relay_duration # If using a pre relay, wait for it to complete before # querying the sensor for a measurement if self.get_new_measurement: if ((self.pre_relay_setup and self.pre_relay_activated and time.time() < self.pre_relay_timer) or not self.pre_relay_setup): # Get measurement(s) from sensor self.updateMeasurement() # Add measurement(s) to influxdb self.addMeasurementInfluxdb() self.pre_relay_activated = False self.get_new_measurement = False # Check sensor conditionals if their timers have expired for each_cond_id in self.cond_id: if self.cond_activated[each_cond_id]: if time.time() > self.cond_timer[each_cond_id]: self.cond_timer[each_cond_id] = time.time()+self.cond_period[each_cond_id] self.checkConditionals(each_cond_id) time.sleep(0.1) self.running = False self.logger.info("[Sensor {}] Deactivated in {}ms".format( self.sensor_id, (timeit.default_timer()-self.thread_shutdown_timer)*1000)) except Exception as msg: self.logger.exception("[Sensor {}] Error: {}".format( self.sensor_id, msg)) def addMeasurementInfluxdb(self): """ Add a measurement entries to InfluxDB :rtype: None """ if self.updateSuccess: data = [] for each_measurement, each_value in self.measurement.values.iteritems(): data.append({ "measurement": each_measurement, "tags": { "device_id": self.sensor_id, "device_type": self.sensor_type }, "fields": { "value": each_value } }) write_db = threading.Thread( target=write_influxdb_list, args=(self.logger, INFLUXDB_HOST, INFLUXDB_PORT, INFLUXDB_USER, INFLUXDB_PASSWORD, INFLUXDB_DATABASE, data,)) write_db.start() 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 updateMeasurement(self): """ Retrieve measurement from sensor :return: None if success, 0 if fail :rtype: int or None """ if not self.device_recognized: self.logger.debug( "[Sensor {}] Device not recognized: {}".format(self.sensor_id, self.device_type)) self.updateSuccess = False return 1 if self.multiplexer: # Acquire a lock for multiplexer self.lock_status, self.lock_response = self.setup_lock(self.multiplexer_address, self.multiplexer_lock_file) if not self.lock_status: self.logger.warning("[Sensor {}] Could not acquire lock " "for multiplexer. Error:" " {}".format(self.sensor_id, self.lock_response)) self.updateSuccess = False return 1 self.logger.debug("[Sensor {}] Setting multiplexer at address {} to " "channel {}".format(self.sensor_id, self.multiplexer_address_string, self.multiplexer_channel)) # Set multiplexer channel self.multiplexer_status, self.multiplexer_response = self.multiplexer.setup(self.multiplexer_channel) if not self.multiplexer_status: self.logger.warning("[Sensor {}] Could not set channel " "with multiplexer at address {}. Error:" " {}".format(self.sensor_id, self.multiplexer_address_string, self.multiplexer_response)) self.updateSuccess = False return 1 if self.adc: try: # Acquire a lock for ADC self.lock_status, self.lock_response = self.setup_lock(int(str(self.location), 16), self.adc_lock_file) if not self.lock_status: self.logger.warning("[Sensor {}] Could not acquire lock " "for multiplexer. Error:" " {}".format(self.sensor_id, self.lock_response)) self.updateSuccess = False return 1 # Get measurement from ADC measurements = self.adc.next() if measurements is not None: # Get the voltage difference between min and max volts diff_voltage = abs(self.adc_volts_max-self.adc_volts_min) # Ensure the measured voltage stays within the min/max bounds if measurements['voltage'] < self.adc_volts_min: measured_voltage = self.adc_volts_min elif measurements['voltage'] > self.adc_volts_max: measured_voltage = self.adc_volts_max else: measured_voltage = measurements['voltage'] # Calculate the percentage of the voltage difference percent_diff = (measured_voltage-self.adc_volts_min)/diff_voltage # Get the units difference between min and max units diff_units = abs(self.adc_units_max-self.adc_units_min) # Calculate the measured units from the percent difference converted_units = self.adc_units_min+(diff_units*percent_diff) if converted_units < self.adc_units_min: measurements[self.adc_measure] = self.adc_units_min elif converted_units > self.adc_units_max: measurements[self.adc_measure] = self.adc_units_max else: measurements[self.adc_measure] = converted_units except Exception as msg: self.logger.exception("[Sensor {}] Error while attempting to read " "adc: {}".format(self.sensor_id, msg)) finally: self.release_lock(int(str(self.location), 16), self.adc_lock_file) else: try: # Get measurement from sensor measurements = self.measure_sensor.next() except Exception as msg: self.logger.exception("[Sensor {}] Error while attempting to read " "sensor: {}".format(self.sensor_id, msg)) if self.multiplexer: self.release_lock(self.multiplexer_address, self.multiplexer_lock_file) if self.device_recognized and measurements is not None: self.measurement = Measurement(measurements) self.updateSuccess = True else: self.updateSuccess = False self.lastUpdate = time.time() def setup_lock(self, i2c_address, lockfile): self.execution_timer = timeit.default_timer() try: self.lock[lockfile] = LockFile(lockfile) while not self.lock[lockfile].i_am_locking(): try: self.logger.debug("[Locking 0x{:02X}] Acquiring Lock: {}".format(i2c_address, self.lock[lockfile].path)) self.lock[lockfile].acquire(timeout=60) # wait up to 60 seconds except: self.logger.exception("[Locking 0x{:02X}] Waited 60 seconds. Breaking lock to acquire {}".format(i2c_address, self.lock[lockfile].path)) self.lock[lockfile].break_lock() self.lock[lockfile].acquire() self.logger.debug("[Locking 0x{:02X}] Acquired Lock: {}".format(i2c_address, self.lock[lockfile].path)) self.logger.debug("[Locking 0x{:02X}] Executed in {}ms".format(i2c_address, (timeit.default_timer()-self.execution_timer)*1000)) return 1, "Success" except Exception as msg: return 0, "Multiplexer Fail: {}".format(msg) def release_lock(self, i2c_address, lockfile): self.logger.debug("[Locking 0x{:02X}] Releasing Lock: {}".format(i2c_address, lockfile)) self.lock[lockfile].release() def getLastMeasurement(self, measurement_type): """ Retrieve the latest sensor measurement :return: The latest sensor value or None if no data available :rtype: float or None :param measurement_type: Environmental condition of a sensor (e.g. temperature, humidity, pressure, etc.) :type measurement_type: str """ last_measurement = read_last_influxdb(INFLUXDB_HOST, INFLUXDB_PORT, INFLUXDB_USER, INFLUXDB_PASSWORD, INFLUXDB_DATABASE, self.sensor_id, measurement_type).raw if last_measurement: number = len(last_measurement['series'][0]['values']) last_value = last_measurement['series'][0]['values'][number-1][1] return last_value else: return None def edge_detected(self, pin): gpio_state = GPIO.input(int(self.location)) if time.time() > self.edge_reset_timer: self.edge_reset_timer = time.time()+self.switch_reset_period if (self.switch_edge == 'rising' or (self.switch_edge == 'both' and gpio_state)): rising_or_falling = 1 else: rising_or_falling = -1 write_db = threading.Thread( target=write_influxdb, args=(self.logger, INFLUXDB_HOST, INFLUXDB_PORT, INFLUXDB_USER, INFLUXDB_PASSWORD, INFLUXDB_DATABASE, self.sensor_type, self.sensor_id, 'edge', rising_or_falling,)) write_db.start() # Check sensor conditionals for each_cond_id in self.cond_id: if ( (self.cond_edge_detected[each_cond_id] == 'rising' and gpio_state) or (self.cond_edge_detected[each_cond_id] == 'falling' and not gpio_state) or self.cond_edge_detected[each_cond_id] == 'both'): self.checkConditionals(each_cond_id) def setup_sensor_conditionals(self, cond_mod='setup', cond_id=None): # Signal to pause the main loop and wait for verification self.pause_loop = True while not self.verify_pause_loop: time.sleep(0.1) if cond_mod == 'del': self.cond_id.pop(cond_id, None) self.cond_activated.pop(cond_id, None) self.cond_period.pop(cond_id, None) self.cond_name.pop(cond_id, None) self.cond_measurement_type.pop(cond_id, None) self.cond_edge_detected.pop(cond_id, None) self.cond_direction.pop(cond_id, None) self.cond_setpoint.pop(cond_id, None) self.cond_relay_id.pop(cond_id, None) self.cond_relay_state.pop(cond_id, None) self.cond_relay_on_duration.pop(cond_id, None) self.cond_execute_command.pop(cond_id, None) self.cond_email_notify.pop(cond_id, None) self.cond_flash_lcd.pop(cond_id, None) self.cond_camera_record.pop(cond_id, None) self.cond_timer.pop(cond_id, None) self.smtp_wait_timer.pop(cond_id, None) self.logger.debug("[Sensor Conditional {}] Deleted Conditional " "from Sensor {}".format(cond_id, self.sensor_id)) else: with session_scope(MYCODO_DB_PATH) as new_session: if cond_mod == 'setup': self.cond_id = {} self.cond_name = {} self.cond_activated = {} self.cond_period = {} self.cond_measurement_type = {} self.cond_edge_detected = {} self.cond_direction = {} self.cond_setpoint = {} self.cond_relay_id = {} self.cond_relay_state = {} self.cond_relay_on_duration = {} self.cond_execute_command = {} self.cond_email_notify = {} self.cond_flash_lcd = {} self.cond_camera_record = {} self.cond_timer = {} self.smtp_wait_timer = {} self.sensor_conditional = new_session.query( SensorConditional).filter( SensorConditional.sensor_id == self.sensor_id) self.sensor_conditional = self.sensor_conditional.filter( SensorConditional.activated == 1) elif cond_mod == 'add': self.sensor_conditional = new_session.query( SensorConditional).filter( SensorConditional.sensor_id == self.sensor_id) self.sensor_conditional = self.sensor_conditional.filter( SensorConditional.activated == 1) self.sensor_conditional = self.sensor_conditional.filter( SensorConditional.id == cond_id) self.logger.debug("[Sensor Conditional {}] Added " "Conditional to Sensor {}".format( cond_id, self.sensor_id)) elif cond_mod == 'mod': self.sensor_conditional = new_session.query( SensorConditional).filter( SensorConditional.sensor_id == self.sensor_id) self.sensor_conditional = self.sensor_conditional.filter( SensorConditional.id == cond_id) self.logger.debug("[Sensor Conditional {}] Modified " "Conditional from Sensor {}".format( cond_id, self.sensor_id)) else: return 1 for each_cond in self.sensor_conditional.all(): if cond_mod == 'setup': self.logger.debug("[Sensor Conditional {}] Activated " "Conditional from Sensor {}".format( each_cond.id, self.sensor_id)) self.cond_id[each_cond.id] = each_cond.id self.cond_name[each_cond.id] = each_cond.name self.cond_activated[each_cond.id] = each_cond.activated self.cond_period[each_cond.id] = each_cond.period self.cond_measurement_type[each_cond.id] = each_cond.measurement_type self.cond_edge_detected[each_cond.id] = each_cond.edge_detected self.cond_direction[each_cond.id] = each_cond.direction self.cond_setpoint[each_cond.id] = each_cond.setpoint self.cond_relay_id[each_cond.id] = each_cond.relay_id self.cond_relay_state[each_cond.id] = each_cond.relay_state self.cond_relay_on_duration[each_cond.id] = each_cond.relay_on_duration self.cond_execute_command[each_cond.id] = each_cond.execute_command self.cond_email_notify[each_cond.id] = each_cond.email_notify self.cond_flash_lcd[each_cond.id] = each_cond.email_notify self.cond_camera_record[each_cond.id] = each_cond.camera_record self.cond_timer[each_cond.id] = time.time()+self.cond_period[each_cond.id] self.smtp_wait_timer[each_cond.id] = time.time()+3600 self.pause_loop = False self.verify_pause_loop = False def isRunning(self): return self.running def stopController(self): self.thread_shutdown_timer = timeit.default_timer() if self.device_type not in ['EDGE', 'ADS1x15', 'MCP342x']: self.measure_sensor.stopSensor() self.running = False
class SensorController(threading.Thread): """ Class for controlling the sensor """ def __init__(self, ready, logger, sensor_id): threading.Thread.__init__(self) list_devices_i2c = [ 'ADS1x15', 'AM2315', 'ATLAS_PT1000', 'BMP', 'HTU21D', 'MCP342x', 'SHT2x', 'TMP006', 'TSL2561' ] self.thread_startup_timer = timeit.default_timer() self.thread_shutdown_timer = 0 self.ready = ready self.logger = logger self.lock = {} self.sensor_id = sensor_id self.control = DaemonControl() self.pause_loop = False self.verify_pause_loop = True self.setup_sensor_conditionals() with session_scope(MYCODO_DB_PATH) as new_session: sensor = new_session.query(Sensor) sensor = sensor.filter(Sensor.id == self.sensor_id).first() self.i2c_bus = sensor.i2c_bus self.location = sensor.location self.device_type = sensor.device self.sensor_type = sensor.device_type self.period = sensor.period self.multiplexer_address_raw = sensor.multiplexer_address self.multiplexer_bus = sensor.multiplexer_bus self.multiplexer_channel = sensor.multiplexer_channel self.adc_channel = 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 = new_session.query(Relay).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 = new_session.query(SMTP).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_type in list_devices_i2c: self.i2c_address = int(str(self.location), 16) # Set up multiplexer if enabled if self.device_type in list_devices_i2c and self.multiplexer_address_raw: self.multiplexer_address_string = self.multiplexer_address_raw self.multiplexer_address = int(str(self.multiplexer_address_raw), 16) self.multiplexer_lock_file = "/var/lock/mycodo_multiplexer_0x{:02X}.pid".format( self.multiplexer_address) self.multiplexer = TCA9548A(self.multiplexer_bus, self.multiplexer_address) else: self.multiplexer = None if self.device_type in ['ADS1x15', 'MCP342x'] and self.location: self.adc_lock_file = "/var/lock/mycodo_adc_bus{}_0x{:02X}.pid".format( self.i2c_bus, self.i2c_address) # Set up edge detection of a GPIO pin if self.device_type == '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_type == 'ADS1x15': self.adc = ADS1x15_read(self.i2c_address, self.i2c_bus, self.adc_channel, self.adc_gain) elif self.device_type == 'MCP342x': self.adc = MCP342x_read(self.i2c_address, self.i2c_bus, self.adc_channel, self.adc_gain, self.adc_resolution) else: self.adc = None self.device_recognized = True # Set up sensor if self.device_type in ['EDGE', 'ADS1x15', 'MCP342x']: self.measure_sensor = None elif self.device_type == 'RPiCPULoad': self.measure_sensor = RaspberryPiCPULoad() elif self.device_type == 'RPi': self.measure_sensor = RaspberryPiCPUTemp() elif self.device_type == 'DS18B20': self.measure_sensor = DS18B20(self.location) elif self.device_type == 'DHT11': self.measure_sensor = DHT11(pigpio.pi(), int(self.location)) elif self.device_type in ['DHT22', 'AM2302']: self.measure_sensor = DHT22(pigpio.pi(), int(self.location)) elif self.device_type == 'HTU21D': self.measure_sensor = HTU21D_read(self.i2c_bus) elif self.device_type == 'AM2315': self.measure_sensor = AM2315_read(self.i2c_bus) elif self.device_type == 'ATLAS_PT1000': self.measure_sensor = Atlas_PT1000(self.i2c_address, self.i2c_bus) elif self.device_type == 'K30': self.measure_sensor = K30() elif self.device_type == 'BMP': self.measure_sensor = BMP(self.i2c_bus) elif self.device_type == 'SHT1x_7x': self.measure_sensor = SHT1x_7x_read(self.location, self.sht_clock_pin, self.sht_voltage) elif self.device_type == 'SHT2x': self.measure_sensor = SHT2x_read(self.i2c_address, self.i2c_bus) elif self.device_type == 'TMP006': self.measure_sensor = TMP006_read(self.i2c_address, self.i2c_bus) elif self.device_type == 'TSL2561': self.measure_sensor = TSL2561_read(self.i2c_address, self.i2c_bus) else: self.device_recognized = False self.logger.debug("[Sensor {}] Device '{}' not " "recognized:".format(self.sensor_id, self.device_type)) raise Exception("{} is not a valid device type.".format( self.device_type)) self.edge_reset_timer = time.time() self.sensor_timer = time.time() self.running = False self.lastUpdate = None def run(self): try: self.running = True self.logger.info("[Sensor {}] Activated in {:.1f} ms".format( self.sensor_id, (timeit.default_timer() - self.thread_startup_timer) * 1000)) self.ready.set() # Set up edge detection if self.device_type == 'EDGE': GPIO.setmode(GPIO.BCM) GPIO.setup(int(self.location), GPIO.IN) GPIO.add_event_detect(int(self.location), self.switch_edge_gpio, callback=self.edge_detected, bouncetime=self.switch_bouncetime) while self.running: # Pause loop to modify conditional statements. # Prevents execution of conditional while varibles are # being modified. if self.pause_loop: self.verify_pause_loop = True while self.pause_loop: time.sleep(0.1) if self.device_type in ['EDGE']: # Sensors that are triggered (simple switch, # PIR motion, reed, hall, etc.) pass else: # Sensors that are read at a regular period # Signal that a measurement needs to be obtained if time.time( ) > self.next_measurement and not self.get_new_measurement: self.get_new_measurement = True self.next_measurement = time.time() + self.period # if signaled and a pre relay is set up correctly, turn the # relay on for the set duration if (self.get_new_measurement and self.pre_relay_setup and not self.pre_relay_activated): relay_on = threading.Thread( target=self.control.relay_on, args=( self.pre_relay_id, self.pre_relay_duration, )) relay_on.start() self.pre_relay_activated = True self.pre_relay_timer = time.time( ) + self.pre_relay_duration # If using a pre relay, wait for it to complete before # querying the sensor for a measurement if self.get_new_measurement: if ((self.pre_relay_setup and self.pre_relay_activated and time.time() < self.pre_relay_timer) or not self.pre_relay_setup): # Get measurement(s) from sensor self.updateMeasurement() # Add measurement(s) to influxdb self.addMeasurementInfluxdb() self.pre_relay_activated = False self.get_new_measurement = False # Check sensor conditionals if their timers have expired for each_cond_id in self.cond_id: if self.cond_activated[each_cond_id]: if time.time() > self.cond_timer[each_cond_id]: self.cond_timer[each_cond_id] = time.time( ) + self.cond_period[each_cond_id] self.checkConditionals(each_cond_id) time.sleep(0.1) self.running = False self.logger.info("[Sensor {}] Deactivated in {:.1f} ms".format( self.sensor_id, (timeit.default_timer() - self.thread_shutdown_timer) * 1000)) except Exception as msg: self.logger.exception("[Sensor {}] Error: {}".format( self.sensor_id, msg)) def addMeasurementInfluxdb(self): """ Add a measurement entries to InfluxDB :rtype: None """ if self.updateSuccess: data = [] for each_measurement, each_value in self.measurement.values.iteritems( ): data.append( format_influxdb_data(self.sensor_type, self.sensor_id, each_measurement, each_value)) write_db = threading.Thread(target=write_influxdb_list, args=( self.logger, INFLUXDB_HOST, INFLUXDB_PORT, INFLUXDB_USER, INFLUXDB_PASSWORD, INFLUXDB_DATABASE, data, )) write_db.start() 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(INSTALL_DIRECTORY, 'photo') elif self.cond_camera_record[cond_id] in ['video', 'videoemail']: attachment_file = camera_record(INSTALL_DIRECTORY, 'video', duration_sec=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() send_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 updateMeasurement(self): """ Retrieve measurement from sensor :return: None if success, 0 if fail :rtype: int or None """ if not self.device_recognized: self.logger.debug("[Sensor {}] Device not recognized: {}".format( self.sensor_id, self.device_type)) self.updateSuccess = False return 1 if self.multiplexer: # Acquire a lock for multiplexer (self.lock_status, self.lock_response) = self.setup_lock(self.multiplexer_address, self.multiplexer_bus, self.multiplexer_lock_file) if not self.lock_status: self.logger.warning("[Sensor {}] Could not acquire lock " "for multiplexer. Error:" " {}".format(self.sensor_id, self.lock_response)) self.updateSuccess = False return 1 self.logger.debug( "[Sensor {}] Setting multiplexer at address {} to " "channel {}".format(self.sensor_id, self.multiplexer_address_string, self.multiplexer_channel)) # Set multiplexer channel (self.multiplexer_status, self.multiplexer_response) = self.multiplexer.setup( self.multiplexer_channel) if not self.multiplexer_status: self.logger.warning("[Sensor {}] Could not set channel " "with multiplexer at address {}. Error:" " {}".format( self.sensor_id, self.multiplexer_address_string, self.multiplexer_response)) self.updateSuccess = False return 1 if self.adc: try: # Acquire a lock for ADC (self.lock_status, self.lock_response) = self.setup_lock(self.i2c_address, self.i2c_bus, self.adc_lock_file) if not self.lock_status: self.logger.warning("[Sensor {}] Could not acquire lock " "for multiplexer. Error:" " {}".format(self.sensor_id, self.lock_response)) self.updateSuccess = False return 1 # Get measurement from ADC measurements = self.adc.next() if measurements is not None: # Get the voltage difference between min and max volts diff_voltage = abs(self.adc_volts_max - self.adc_volts_min) # Ensure the measured voltage stays within the min/max bounds if measurements['voltage'] < self.adc_volts_min: measured_voltage = self.adc_volts_min elif measurements['voltage'] > self.adc_volts_max: measured_voltage = self.adc_volts_max else: measured_voltage = measurements['voltage'] # Calculate the percentage of the voltage difference percent_diff = (measured_voltage - self.adc_volts_min) / diff_voltage # Get the units difference between min and max units diff_units = abs(self.adc_units_max - self.adc_units_min) # Calculate the measured units from the percent difference converted_units = self.adc_units_min + (diff_units * percent_diff) if converted_units < self.adc_units_min: measurements[self.adc_measure] = self.adc_units_min elif converted_units > self.adc_units_max: measurements[self.adc_measure] = self.adc_units_max else: measurements[self.adc_measure] = converted_units except Exception as msg: self.logger.exception( "[Sensor {}] Error while attempting to read " "adc: {}".format(self.sensor_id, msg)) finally: self.release_lock(self.i2c_address, self.i2c_bus, self.adc_lock_file) else: try: # Get measurement from sensor measurements = self.measure_sensor.next() except Exception as msg: self.logger.exception( "[Sensor {}] Error while attempting to read " "sensor: {}".format(self.sensor_id, msg)) if self.multiplexer: self.release_lock(self.multiplexer_address, self.multiplexer_bus, self.multiplexer_lock_file) if self.device_recognized and measurements is not None: self.measurement = Measurement(measurements) self.updateSuccess = True else: self.updateSuccess = False self.lastUpdate = time.time() def setup_lock(self, i2c_address, i2c_bus, lockfile): self.execution_timer = timeit.default_timer() try: self.lock[lockfile] = LockFile(lockfile) while not self.lock[lockfile].i_am_locking(): try: self.logger.debug("[Locking bus-{} 0x{:02X}] Acquiring " "Lock: {}".format( i2c_bus, i2c_address, self.lock[lockfile].path)) self.lock[lockfile].acquire( timeout=60) # wait up to 60 seconds except: self.logger.exception( "[Locking bus-{} 0x{:02X}] Waited 60 " "seconds. Breaking lock to acquire " "{}".format(i2c_bus, i2c_address, self.lock[lockfile].path)) self.lock[lockfile].break_lock() self.lock[lockfile].acquire() self.logger.debug( "[Locking bus-{} 0x{:02X}] Acquired Lock: {}".format( i2c_bus, i2c_address, self.lock[lockfile].path)) self.logger.debug( "[Locking bus-{} 0x{:02X}] Executed in {:.1f} ms".format( i2c_bus, i2c_address, (timeit.default_timer() - self.execution_timer) * 1000)) return 1, "Success" except Exception as msg: return 0, "Multiplexer Fail: {}".format(msg) def release_lock(self, i2c_address, i2c_bus, lockfile): self.logger.debug( "[Locking bus-{} 0x{:02X}] Releasing Lock: {}".format( i2c_bus, i2c_address, lockfile)) self.lock[lockfile].release() def getLastMeasurement(self, measurement_type): """ Retrieve the latest sensor measurement :return: The latest sensor value or None if no data available :rtype: float or None :param measurement_type: Environmental condition of a sensor (e.g. temperature, humidity, pressure, etc.) :type measurement_type: str """ last_measurement = read_last_influxdb(INFLUXDB_HOST, INFLUXDB_PORT, INFLUXDB_USER, INFLUXDB_PASSWORD, INFLUXDB_DATABASE, self.sensor_id, measurement_type, self.period / 60 * 1.5).raw if last_measurement: number = len(last_measurement['series'][0]['values']) last_value = last_measurement['series'][0]['values'][number - 1][1] return last_value else: return None def edge_detected(self, pin): gpio_state = GPIO.input(int(self.location)) if time.time() > self.edge_reset_timer: self.edge_reset_timer = time.time() + self.switch_reset_period if (self.switch_edge == 'rising' or (self.switch_edge == 'both' and gpio_state)): rising_or_falling = 1 else: rising_or_falling = -1 write_db = threading.Thread(target=write_influxdb_value, args=( self.logger, INFLUXDB_HOST, INFLUXDB_PORT, INFLUXDB_USER, INFLUXDB_PASSWORD, INFLUXDB_DATABASE, self.sensor_type, self.sensor_id, 'edge', rising_or_falling, )) write_db.start() # Check sensor conditionals for each_cond_id in self.cond_id: if ((self.cond_edge_detected[each_cond_id] == 'rising' and gpio_state) or (self.cond_edge_detected[each_cond_id] == 'falling' and not gpio_state) or self.cond_edge_detected[each_cond_id] == 'both'): self.checkConditionals(each_cond_id) def setup_sensor_conditionals(self, cond_mod='setup', cond_id=None): # Signal to pause the main loop and wait for verification self.pause_loop = True while not self.verify_pause_loop: time.sleep(0.1) if cond_mod == 'del': self.cond_id.pop(cond_id, None) self.cond_activated.pop(cond_id, None) self.cond_period.pop(cond_id, None) self.cond_name.pop(cond_id, None) self.cond_measurement_type.pop(cond_id, None) self.cond_edge_detected.pop(cond_id, None) self.cond_direction.pop(cond_id, None) self.cond_setpoint.pop(cond_id, None) self.cond_relay_id.pop(cond_id, None) self.cond_relay_state.pop(cond_id, None) self.cond_relay_on_duration.pop(cond_id, None) self.cond_execute_command.pop(cond_id, None) self.cond_email_notify.pop(cond_id, None) self.cond_flash_lcd.pop(cond_id, None) self.cond_camera_record.pop(cond_id, None) self.cond_timer.pop(cond_id, None) self.smtp_wait_timer.pop(cond_id, None) self.logger.debug("[Sensor Conditional {}] Deleted Conditional " "from Sensor {}".format(cond_id, self.sensor_id)) else: with session_scope(MYCODO_DB_PATH) as new_session: if cond_mod == 'setup': self.cond_id = {} self.cond_name = {} self.cond_activated = {} self.cond_period = {} self.cond_measurement_type = {} self.cond_edge_detected = {} self.cond_direction = {} self.cond_setpoint = {} self.cond_relay_id = {} self.cond_relay_state = {} self.cond_relay_on_duration = {} self.cond_execute_command = {} self.cond_email_notify = {} self.cond_flash_lcd = {} self.cond_camera_record = {} self.cond_timer = {} self.smtp_wait_timer = {} self.sensor_conditional = new_session.query( SensorConditional).filter( SensorConditional.sensor_id == self.sensor_id) self.sensor_conditional = self.sensor_conditional.filter( SensorConditional.activated == 1) elif cond_mod == 'add': self.sensor_conditional = new_session.query( SensorConditional).filter( SensorConditional.sensor_id == self.sensor_id) self.sensor_conditional = self.sensor_conditional.filter( SensorConditional.activated == 1) self.sensor_conditional = self.sensor_conditional.filter( SensorConditional.id == cond_id) self.logger.debug("[Sensor Conditional {}] Added " "Conditional to Sensor {}".format( cond_id, self.sensor_id)) elif cond_mod == 'mod': self.sensor_conditional = new_session.query( SensorConditional).filter( SensorConditional.sensor_id == self.sensor_id) self.sensor_conditional = self.sensor_conditional.filter( SensorConditional.id == cond_id) self.logger.debug("[Sensor Conditional {}] Modified " "Conditional from Sensor {}".format( cond_id, self.sensor_id)) else: return 1 for each_cond in self.sensor_conditional.all(): if cond_mod == 'setup': self.logger.debug("[Sensor Conditional {}] Activated " "Conditional from Sensor {}".format( each_cond.id, self.sensor_id)) self.cond_id[each_cond.id] = each_cond.id self.cond_name[each_cond.id] = each_cond.name self.cond_activated[each_cond.id] = each_cond.activated self.cond_period[each_cond.id] = each_cond.period self.cond_measurement_type[ each_cond.id] = each_cond.measurement_type self.cond_edge_detected[ each_cond.id] = each_cond.edge_detected self.cond_direction[each_cond.id] = each_cond.direction self.cond_setpoint[each_cond.id] = each_cond.setpoint self.cond_relay_id[each_cond.id] = each_cond.relay_id self.cond_relay_state[each_cond.id] = each_cond.relay_state self.cond_relay_on_duration[ each_cond.id] = each_cond.relay_on_duration self.cond_execute_command[ each_cond.id] = each_cond.execute_command self.cond_email_notify[ each_cond.id] = each_cond.email_notify self.cond_flash_lcd[each_cond.id] = each_cond.email_notify self.cond_camera_record[ each_cond.id] = each_cond.camera_record self.cond_timer[each_cond.id] = time.time( ) + self.cond_period[each_cond.id] self.smtp_wait_timer[each_cond.id] = time.time() + 3600 self.pause_loop = False self.verify_pause_loop = False def isRunning(self): return self.running def stopController(self): self.thread_shutdown_timer = timeit.default_timer() if self.device_type not in ['EDGE', 'ADS1x15', 'MCP342x']: self.measure_sensor.stopSensor() self.running = False