from bokeh.models import Panel
from bokeh.layouts import column
from bokeh.models import Div
from os.path import dirname, join
import os
import sys
import subprocess
import socket
import pyudev
[docs]class InfoTab:
"""Class to create a Bokeh tab that displays information about the system and the peripherical devices.
:param init_dev: picamera and audio device object initialized in :mod:`sensors.app_hooks` \
with :class:`sensors.Utilities.initdevices.InitDevices`
:type init_dev: object
"""
def __init__(self, config):
self.config = config
self.camera = config.camera
self.audio_devices = config.audio_devices
self.system_info = Div(text=self.get_system_info(),
render_as_text=False, width=1000)
self.camera_info = Div(text=self.get_camera_info(),
render_as_text=False, width=1000)
self.audio_info = Div(text=self.get_audio_info(),
render_as_text=False, width=1000)
self.sensors_info = Div(text=self.get_sensors_info(),
render_as_text=False, width=1000)
# self.storage_info = Div(text=self.get_storage_info(),
# render_as_text=False, width=1000)
# Information tab header loaded from html template
filename = join(dirname(__file__), "info_template.html")
desc = Div(text=open(filename).read(),
render_as_text=False, width=800)
layout = column(desc, self.system_info, self.camera_info, self.audio_info, self.sensors_info)
# Make a tab with the layout
self._tab = Panel(child=layout, title='Information')
@property
def tab(self):
"""Creates and arranges the elements of the corresponding Bokeh tab.
:return: Bokeh panel for visualization of general information on system and devices
:rtype: Bokeh object
"""
return self._tab
[docs] def get_system_info(self):
"""Creates general system information.
:return: html code for corresponding information
:rtype: string
"""
info = '<h2>System and User Information</h2>\n'
info += '<table style="width:100%; margin: 0 20px 0 0">'
info += '<tr><td>Operating system: </td><td>'f'{sys.platform}''</td></tr>'
info += '<tr><td>Python version:</td><td>' + sys.version.replace('\n', '') + '</td></tr>'
info += '<tr><td>Hostname:</td><td>'f'{socket.gethostname()}''</td></tr>'
try:
login = f'{os.getlogin()}'
except Exception as e:
login = ''
info += '<tr><td>Login name:</td><td>' + login + '</td></tr>'
if self.config.custom_config_filename is None:
custom_config_filename = 'Default <i>config.yaml</i> file loaded! A <i>custom_config.yaml</i> has been ' \
'created in <i>/sensors/custom_config_files</i>!<br>If you rename it, change ' \
'the <i>custom_config_filename</i> in the default config.yaml in ' \
'<i>/sensors</i>! ' \
'<br><b>Please change your settings in your custom config file!</b>'
else:
custom_config_filename = self.config.custom_config_filename
info += '<tr><td>Configuration file: </td><td>' + custom_config_filename + '</td></tr>'
KInsect_web_app = '<a href="https://webapp-test.kinsecta.org/login">KInsecta Web App</a>'
if self.config.user_id is None:
user_id = 'Not set yet! Please register to the ' + KInsect_web_app + ' to get assigned an ID and ' \
'change the user_id in your <br> custom config.yaml file accordingly!'
else:
user_id = f'{self.config.user_id}'
info += '<tr><td>User ID:</td><td>' + user_id + '</td></tr>'
if self.config.sensor_id is None:
sensor_id = 'Not set yet! After your first data upload to the ' + KInsect_web_app + ' a new sensor ID ' \
'will be assigned to this sensor. <br> Please change the sensor_id in your custom ' \
'config.yaml file accordingly!'
else:
sensor_id = f'{self.config.sensor_id}'
info += '<tr><td>Sensor ID:</td><td>' + sensor_id + '</td></tr>'
if self.config.name is None or self.config.postcode is None:
name_postcode = 'Not set yet! Please provide information about the sensor location and its postcode in ' \
'your custom config.yaml file!'
else:
name_postcode = f'{self.config.name}, {self.config.postcode}'
info += '<tr><td>Sensor location:</td><td>' + name_postcode + '</td></tr>'
info += '<tr><td>Masked GPS data:</td><td>'f'{self.config.gps_masked}''</td></tr>'
if self.config.latitude is None or self.config.latitude is None:
gps_data = 'Not set yet! Please provide GPS information in your custom config.yaml!'
else:
gps_data = f'{self.config.latitude}, {self.config.longitude}'
info += '<tr><td>GPS data:</td><td>' + gps_data + '</td></tr>'
info += '</table>'
return info
[docs] def get_camera_info(self):
"""Creates PiCamera information.
:return: html code for corresponding information
:rtype: string
"""
info = '<h2>Camera Information</h2>\n'
if self.camera is None:
info = '<h2>No PiCamera found!<h2>'
else:
resolution = self.camera.MAX_RESOLUTION
info += '<table style="width:100%; margin: 0 20px 0 0">'
info += '<tr><td>PiCamera type:</td><td>'f'{self.camera.revision}''</td></tr>'
info += '<tr><td>Maximum resolution:</td><td>'f'{resolution.width} x {resolution.height}''</td></tr>'
info += '</table>'
return info
[docs] def get_audio_info(self):
"""Creates audio system information.
:return: html code for corresponding information
:rtype: string
"""
info = '<h2>Audio Device Information</h2>\n'
speaker, mic = self.audio_devices
info += '<table style="width:100%; margin: 0 20px 0 0">'
if mic is not None:
info += '<tr><td>Microphone input ('f'{mic.channels}'' channels):</td><td>'f'{mic.id}''</td></tr>'
else:
info += '<tr><td>No USB microphone found!</td></tr>'
if speaker is not None:
info += '<tr><td>Speaker output ('f'{speaker.channels}'' channels):</td><td>'f'{speaker.id}''</td></tr>'
else:
info += '<tr><td>No USB speaker found!</td></tr>'
info += '</table>'
return info
[docs] def get_sensors_info(self):
"""Creates information about available sensors for measurement of meta data.
:return: html code for corresponding information
:rtype: string
"""
info = '<h2>Environmental Sensors Information</h2>\n'
info += '<table style="width:100%; margin: 0 20px 0 0">'
url_bme280 = '<a href="https://www.raspberrypi-spy.co.uk/2016/07/' \
'using-bme280-i2c-temperature-pressure-sensor-in-python/">BME280</a>'
if self.config.bme280_sensor[2] is not None:
info += '<tr><td>Humidity sensor:</td><td>' + url_bme280 + '</td></tr>'
else:
info += '<tr><td>Humidity sensor:</td><td>n.a. (for more info visit ' + url_bme280 + ')</td></tr>'
url_bh1750 = '<a href="https://learn.adafruit.com/adafruit-bh1750-ambient-light-sensor/python-circuitpython">' \
'BH1750</a>'
if self.config.bh1750_sensor is not None:
info += '<tr><td>Ambient light sensor:</td><td>' + url_bh1750 + '</td></tr>'
else:
info += '<tr><td>Ambient light sensor:</td><td>n.a. (for more info visit ' + url_bh1750 + ')</td></tr>'
url_as7341 = '<a href="https://learn.adafruit.com/adafruit-as7341-10-channel-light-color-sensor-breakout/' \
'python-circuitpython">AS7341</a>'
if self.config.as7341_sensor is not None:
info += '<tr><td>Spectral color sensor:</td><td>' + url_as7341 + '</td></tr>'
else:
info += '<tr><td>Spectral color sensor:</td><td>n.a. (for more info visit ' + url_as7341 + ')</td></tr>'
url_pmsa003i = '<a href="https://learn.adafruit.com/pmsa003i/python-circuitpython">PMSA003I</a>'
if self.config.pmsa003i_sensor is not None:
info += '<tr><td>Particulate matter sensor:</td><td>' + url_pmsa003i + '</td></tr>'
else:
info += '<tr><td>Particulate matter sensor:</td><td>n.a. ' \
'(for more info visit ' + url_pmsa003i + ')</td></tr>'
info += '</table>'
return info