usense_temphumi_graph_data() (modules.dynamic.DynamicIoT method)

email.message.Message: Representing an email message using the compat32 API

What is the most efficient way to concatenate many strings together?

Why does a_tuple[i] += [‘item’] raise an exception when the addition works?

What does the slash(/) in the parameter list of a function mean?

PEP 3118: New memoryview implementation and buffer protocol documentation

PEP 476: Enabling certificate verification by default for stdlib http clients

PEP 471 - os.scandir() function – a better and faster directory iterator

Why must ‘self’ be used explicitly in method definitions and calls?

argparse — Parser for command-line options, arguments and sub-commands

functools — Higher-order functions and operations on callable objects

sysconfig — Provide access to Python’s configuration information

#!/usr/bin/python3
import os
import sys
import cherrypy
import influxdb
import time
import datetime
import json
from pprint import pprint
from cherrypy.lib.httputil import parse_query_string

# Universal variables
_SCRIPT_PATH = os.path.dirname(sys.argv[0])

influx_host = 'localhost'
influx_port = 8086
influx_user = 'pi'
influx_pwd = 'Ventil6996'
influx_db = 'voltage'
influx_weather_db = 'weather_v2'
influx_IoT_db = 'weather_v2'
influx_status_db = 'status'
height_above_sea_level = 452

variables_known = ["range",
                   "granularity",
                   "start",
                   "end",
                   "type"]
default_variables = {"range": "1h",
                     "granularity": "30s",
                     "end": "1s",
                     "type": "none"}

#------------------------------------------------------------------------------
# Generic Functions

def check_GET(arguments):
    '''Validates html query

    Takes the key-val pairs and evaluates them against those variables, that
    are defined as known to be safe to be parsed.

    ::
    
        Args:
            str(arguments):
            "A string of options to be directly evaluated as a dictionary"

    ::
    
        Returns:
            list(str(variable)):
            "List of strings, occupied by variables validated to be safe"

    '''
    _q = eval(str(arguments))
    keys_to_process = {
        key:_q[key] for key in _q.keys() if key in variables_known}
    resulting_variables = default_variables.copy()
    resulting_variables.update(keys_to_process)
    return resulting_variables

# Classes
class Helper(object):
    def __init__(self):
        '''Helping calculations and functions

        Some generic helpers, that do additional calculations, such as
        percentages, predictions etc.
        '''
        pass

    def pressure_to_std_atm(self, raw_pressure, temperature, hasl):
        a2ts = raw_pressure \
             + ((raw_pressure * 9.80665 * hasl)\
             / (287 * (273 + temperature + (hasl/400))))

        #a2ts = raw_pressure + hasl/10
        return a2ts

    def percentageCalc(self, voltage, system):
        ''' Turns current charge for lead acid batteries into a human
        readable %

        Args:
            float(voltage): Voltage in V
            int(system): nominal system voltage, e.g. 12, 24, 48 etc

        Returns:
            float(percentage): Two decimal state of battery in percentage
        '''
        if system is 12:
            percentage = round(24.5724168782\
                       * voltage * voltage - 521.9890329784 * voltage\
                       + 2771.1828105637, 1)
        elif system is 24:
            percentage = 2.4442 * voltage * voltage\
                      - 82.004 * voltage + 602.91
        elif system is 48:
            # percentage = round((voltage - 46.5) * 18.87, 2)
            percentage = round((voltage - 46.5) * 23.26, 2)
        percentage = 100.00 if percentage > 100.00 else percentage
        percentage = 0 if percentage <= 0 else percentage
        return percentage

class DynamicEnergy(object):
        ''' Parse readings from victron MPPT via Ve.Direct stored in
        InfluxDB
        '''
        self.influx_client = influxdb.client.InfluxDBClient(
            influx_host, influx_port, influx_user, influx_pwd, influx_db
        )
        self.helpers = Helper()

    def FreshValues(self, **kwargs):
        ''' Get most up-to-date energy reading.

            returns:
                dict(): {'state': str(),
                         'time': 'YYYY-mm-DDTHH:MM:SS.149636706Z',
                         'V_unit1': float(mV),
                         'Psol': fint(W),
                         'V_array': int(mV),
                         'ChCurr': int(mV)}
        '''
        bat_query = "SELECT voltage FROM mppt WHERE type = 'bat'" \
                  + "ORDER BY time DESC LIMIT 1"
        voltage_battery = self.influx_client.query(bat_query)

        query = "SELECT last(*) "\
              + "FROM mppt "\
              + "WHERE time > now() - 10m "\
              + "GROUP BY type fill(0)"
        state_query = "SELECT voltage,state FROM mppt "\
                    + "ORDER BY time DESC LIMIT 1"
        state_db_result = self.influx_client.query(state_query)
        state = state_db_result.get_points(measurement='mppt')
        states = state.send(None)
        
        db_result = self.influx_client.query(query)
        bat = db_result.get_points(measurement='mppt',
                                   tags={'type': 'bat'})
        day = db_result.get_points(measurement='mppt',
                                   tags={'type': 'day'})
        prev_day = db_result.get_points(measurement='mppt',
                                   tags={'type': 'prev_day'})                           
        solar = db_result.get_points(measurement='mppt',
                                   tags={'type': 'solar'})
        total = db_result.get_points(measurement='mppt',
                                   tags={'type': 'total'})                           
        bats = bat.send(None)
        solars = solar.send(None)
        days = day.send(None)
        prev_days = prev_day.send(None)
        totals = total.send(None)

        result = {}
        t_stamp = time.strptime(bats['time'].split('.')[0],
                                       "%Y-%m-%dT%H:%M:%S")
        result['time'] = time.strftime("%d.%m.%Y %H:%M:%S", t_stamp)
        result['state'] = states['state']
        result['ChCurr'] = bats['last_current']
        result['V_array'] = bats['last_voltage']
        result['perc_array'] = self.helpers.percentageCalc(
            bats['last_voltage'],
            48)
        result['Psol'] = solars['last_power']
        result['Vsol'] = solars['last_voltage']
        result['Wh_day'] = days['last_Wh']
        result['Pmax_prev_day'] = days['last_Pmax']
        result['Wh_prev_day'] = prev_days['last_Wh']
        result['Pmax_day'] = prev_days['last_Pmax']
        result['total_Wh'] = totals['last_Wh']

        return result

    def stat_values(self, **kwargs):
        '''Max power and daily generation.
        '''
        # GET variables now set, ready to reference them
        _days = kwargs['days'] + 1
        date = datetime.datetime.strftime(datetime.datetime.now(),"%Y-%m-%d")
        measures = []
        result = []
        query = "SELECT mean(power)*24 as DayWh, max(Pmax) AS Pmax "\
              + "FROM mppt WHERE type='solar' OR type='day' "\
              + "AND time > '{} 00:00:00' - {}d "\
              + "GROUP BY time(1d) fill(previous)"\
              + "ORDER BY time DESC"
        query = query.format(date, _days)
        measure = self.influx_client.query(query)
        tm = []
        P_max = []
        Wh_sol = []

        for datapoint in measure['mppt']:
            row = []
            tstamp = datapoint['time']
            Pmax = datapoint["Pmax"] - 10
            DayWh = datapoint["DayWh"]
            result.append([tstamp[:10], Pmax, int(DayWh)])
        return result[:-1]

    def solar_graph_data(self, **q):
        '''
        Function to get solar readings from InfluxDB.
        These parsed into a CSV

        yields: csv in raw, text format
            time,V_solar,I_solar,P_solar,V_array
        '''
        mppt_query_str = "SELECT min(power) AS Pmin, max(power) AS Pmax," \
                       + "mean(power) AS Pavg "\
                       + "FROM mppt WHERE type='solar' "\
                       + "AND time > now() - {} "\
                       + "GROUP BY time({}) fill(previous)"\
                       + "ORDER BY time ASC"
        mppt_query = mppt_query_str.format(q['range'],
                                           q['end'],
                                           q['granularity'])
        query = "SELECT mean(voltage) as Usol, " \
              + "mean(power) as Psol, mean(power)/mean(voltage) as Isol " \
              + "FROM mppt WHERE type='solar' " \
              + "AND time > NOW() - {} " \
              + "AND time < NOW() - {} " \
              + "GROUP BY time({}) fill(previous) ORDER BY time DESC"
        query = query.format(q['range'],
                             q['end'],
                             q['granularity'])                                   
        bat_volt_query = "SELECT mean(voltage) as Varr "\
                       + "FROM mppt WHERE type='bat' "\
                       + "AND time > NOW() - {} "\
                       + "AND time < NOW() - {} "\
                       + "GROUP BY time({}) "\
                       + "FILL(previous) "\
        bat_volt_query = bat_volt_query.format(q['range'],
                                               q['end'],
                                               q['granularity'])
        bat_measures = self.influx_client.query(bat_volt_query)
        measures = self.influx_client.query(query)
        mppt_measures = self.influx_client.query(mppt_query)
        header = "time,V_solar,I_solar,P_solar,V_array\n"
        yield header
        volt_dict = {}
        for voltage in bat_measures["mppt"]:
            volt_dict.update({voltage["time"]: voltage["Varr"]})
        for datapoint in measures["mppt"]:
            tm = str(datapoint["time"]).strip()
            solar_voltage = str(datapoint["Usol"]).strip()
            solar_current = str(datapoint["Isol"]).strip()
            try:
                solar_power = int(datapoint["Psol"])
                solar_power = solar_power - 11 if solar_power > 11 else 0
                solar_power = str(solar_power).strip()
            except:
                solar_power = str(datapoint["Psol"])
            array_voltage = str(volt_dict[tm])
            yield "{},{},{},{},{}\n".format(tm, solar_voltage,
                                             solar_current, solar_power,
                                             array_voltage)

class Expose(object):
        ''' Default expose class. Takes other classes and exposes them
        via html. GET checks should be defined here.
        Function args and kwargs don't need to be checked, as they are internal
        and not exposed.
        '''
        self.energy = DynamicEnergy()
        self.weather = DynamicWeather()
        self.status = DynamicStatus()
        self.iot = DynamicIoT()
        
    @cherrypy.expose
    def index(self):
        return "Index, MOFO"

    def solar_realtime_data(self, **kwargs):
        q = check_GET(kwargs)
        res_type = q['type']
        result = self.energy.FreshValues()
        if res_type == 'json':
            return json.dumps(result)
        else:
            return result
        
    def solar_monitor(self, **kwargs):
        sol_monitor = self.energy.solar_graph_data(**q)
        return sol_monitor
        
    def wind_monitor(self, **kwargs):
        wind_monitor = self.weather.wind_graph_data(**q)
        header = "time,Speed,Gusts,Direction\n"
        for speed, gust, direction in zip(wind_monitor['w_speeds'],
                                          wind_monitor['w_gusts'],
                                          wind_monitor['w_dirs']):
            yield "{},{},{},{}\n".format(speed['time'],
                                         speed['value'],
                                         gust['value'],
                                         direction['value'])  

    def temphumi_monitor(self, **kwargs):
        temphumi_monitor = self.weather.temphumi_graph_data(**q)
        header = "time,T(ins),T(out),Humi(ins),Humi(out)\n"
        for Thin, Thout in zip(temphumi_monitor['th_ins'],
                               temphumi_monitor['th_outs']):
            tm_temp = str(Thin["time"]).strip()
            temp_in_val = str(Thin["Temp"]).strip()
            temp_out_val = str(Thout["Temp"]).strip()
            hum_in_val = str(Thin["Hum"]).strip()
            hum_out_val = str(Thout["Hum"]).strip()
            yield "{},{},{},{},{}\n".format(tm_temp,
                                            temp_in_val,
                                            temp_out_val,
                                            hum_in_val,
                                            hum_out_val)

    def usense_temphumi_monitor(self, **kwargs):
        usense_temphumi_monitor = self.iot.usense_temphumi_graph_data(**q)
        header = "time,T(tea),Humi(tea)\n"
        for temp_humi in usense_temphumi_monitor['temphumis']:
            tm_temp = temp_humi["time"]
            temp_tea_val = temp_humi["temp"]
            hum_tea_val = temp_humi["hum"]
            yield "{},{},{}\n".format(tm_temp, temp_tea_val, hum_tea_val)

    def pressure_monitor(self, **kwargs):
        pressure_monitor = self.weather.pressure_graph_data(**q)
        header = "time,Pressure\n"
        for Press, Tout in zip(pressure_monitor["press_raws"],
                             pressure_monitor["Touts"]):
            tm = str(Press["time"]).strip()
            pressure = self.helpers.pressure_to_std_atm(Press["pressure"],
                                                        Tout["Tout"],
                                                        height_above_sea_level)
            yield "{},{}\n".format(tm, pressure)

    def cpumem_monitor(self, **kwargs):
        cpumem_monitor = self.status.cpumem_graph_data(**q)
        header = "time,Cpu,Mem,Disk\n"
        for cpu, disk, mem in zip(cpumem_monitor["cpus"],
                                  cpumem_monitor["disks"],
                                  cpumem_monitor["mems"]):
            tm = str(cpu["time"]).strip()
            cpu = str(cpu["usage"]).strip()
            mem = str(mem["usage"]).strip()
            disk = str(disk["usage"]).strip()
            yield "{},{},{},{}\n".format(tm, cpu, mem, disk)

    def network_monitor(self, **kwargs):
        network_monitor = self.status.network_graph_data(**q)
        header = "time,Bin,Bout,Din,Dout,Ein,Eout\n"
        for traf in network_monitor["traffic"]:
            tm = str(traf["time"]).strip()
            Bin = str(traf["traffic_b_in"]/1024).strip()
            Bout = str(traf["traffic_b_out"]/1024).strip()
            Din = str(traf["traffic_drop_in"]).strip()
            Dout = str(traf["traffic_drop_out"]).strip()
            Ein = str(traf["traffic_e_in"]).strip()
            Eout = str(traf["traffic_e_out"]).strip()
            yield "{},{},{},{},{},{},{}\n".format(
                tm,
                Bin,
                Bout,
                Din,
                Dout,
                Ein,
                Eout
            )

    def solcap_monitor(self, **kwargs):
        solcap_monitor = self.status.solcap_graph_data(**q)
        header = "time,Solar,Capacitor\n"
        for Sol, Cap in zip(solcap_monitor['sols'],
                            solcap_monitor['caps']):
            tm = str(Sol["time"]).strip()
            solar_value = float(str(Sol["voltage"]).strip()) / 100
            cap_value = float(str(Cap["voltage"]).strip())
            yield "{},{},{}\n".format(tm, solar_value, cap_value)

    def esp_battery_monitor(self, **kwargs):
        place_tag = q['type']
        esp_bat_monitor = self.iot.esp_battery_graph_data(**q)
        header = "time,batt({_place_tag}),batt_perc({_place_tag})\n".format(
            _place_tag=place_tag
        )
        for batt in esp_bat_monitor['volts']:
            tm_batt = str(batt["time"]).strip()
            try:
                batt_val = str(float(batt["voltage"]) / 1023).strip()
            except:
                batt_val = 0
            batt_perc = 4.39
            yield "{},{},{}\n".format(tm_batt, batt_val, batt_perc)
                
class DynamicStatus(object):
        self.influx_status_client = influxdb.client.InfluxDBClient(
            influx_host,
            influx_port,
            influx_user,
            influx_pwd,
            influx_status_db
        )
        
    def cpumem_graph_data(self, **q):
        '''
        Function to get RasPi value readings from InfluxDB.
        '''
        query = "SELECT mean(usage) AS usage "\
              + "FROM RasPI "\
              + "WHERE time > NOW() - {} "\
              + "GROUP BY time({}), type "\
              + "FILL(previous) "\
              + "ORDER BY time ASC"
                             q['end'],
                             q['granularity'])
        db_results = self.influx_status_client.query(query)
        cpu = db_results.get_points(measurement='RasPI',
                                        tags={'type': 'cpu'})
        disk = db_results.get_points(measurement='RasPI',
                                      tags={'type': 'disk'})
        mem = db_results.get_points(measurement='RasPI',
                                       tags={'type': 'mem'})
        cpus = [cpu_use for cpu_use in cpu]
        disks = [disks_use for disks_use in disk]
        mems = [mems_use for mems_use in mem]
        # Let's get the data from DB
        result = {'cpus':cpus, 'disks':disks, 'mems':mems}

    def network_graph_data(self, **q):
        '''
        '''
        query = "SELECT derivative(max(*)) AS traffic "\
              + "FROM net "\
                             q['end'],
        traffic = db_results.get_points(measurement='net',
                                        tags={'type': q['type']})

        traffics = [traf for traf in traffic]
        result = {'traffic': traffics}

    def solcap_graph_data(self, **q):
        '''
        '''
        query = "SELECT mean(voltage) as voltage "\
              + "FROM iss "\
                             q['end'],
        sol = db_results.get_points(measurement='iss',
                                      tags={'type': 'solar'})
        cap = db_results.get_points(measurement='iss',
                                      tags={'type': 'capcaitor'})

        sols = [solar for solar in sol]
        caps = [capacitor for capacitor in cap]
        result = {'sols': sols, 'caps': caps}

class DynamicIoT(object):
    ''' All IoT related methods. Currently, this goes to the weatherDB,
    but is subject to change in the future. 
    '''
        self.influx_iot_client = influxdb.client.InfluxDBClient(
            influx_IoT_db
        )

    def esp_battery_graph_data(self, **q):
        '''
        '''
        query = "SELECT mean(battery) as voltage "\
              + "FROM usense "\
                             q['end'],
        db_results = self.influx_iot_client.query(query)
        volt = db_results.get_points(measurement='usense',
                                      tags={'type': q['type']})

        volts = [bat_volt for bat_volt in volt]
        result = {'volts': volts}

    def usense_temphumi_graph_data(self, **q):
        '''
        '''
        query = "SELECT mean(temperature) as temp, "\
              + "mean(humidity) as hum "\
                             q['end'],
        temphumi = db_results.get_points(measurement='usense',

        temphumis = [temp_humi for temp_humi in temphumi]
        result = {'temphumis': temphumis}

class DynamicWeather(object):
    '''Weather reports, taken dynamically from InfluxDB.
    All measurements tailored to suit the Davis Vantage Vue weather
    station'''
        self.influx_weather_client = influxdb.client.InfluxDBClient(
            influx_weather_db
        )
        
    def wind_graph_data(self, **q):
        '''Function to get wind value readings from InfluxDB.

            time, Speed, Gusts, Direction
        '''
        query = "SELECT mean(value) AS value FROM wind "\
                             q['end'],
        db_results = self.influx_weather_client.query(query)
        w_speed = db_results.get_points(measurement='wind',
                                        tags={'type': 'speed'})
        w_dir = db_results.get_points(measurement='wind',
                                      tags={'type': 'direction'})
        w_gust = db_results.get_points(measurement='wind',
                                       tags={'type': 'windgust'})
        w_speeds = [speed for speed in w_speed]
        w_dirs = [direction for direction in w_dir]
        w_gusts = [gust for gust in w_gust]
        result = {'w_speeds':w_speeds, 'w_dirs':w_dirs, 'w_gusts':w_gusts}

    def temphumi_graph_data(self, **q):
        '''Function to get ttmper and humidity value readings from InfluxDB.

        '''
        query = "SELECT mean(humidity) AS Hum, "\
              + "mean(temperature) AS Temp "\
              + "FROM temphumi "\
                             q['end'],
        th_in = db_results.get_points(measurement='temphumi',
                                        tags={'type': 'internal'})
        th_out = db_results.get_points(measurement='temphumi',
                                      tags={'type': 'external'})

        th_ints = [temphumi_in for temphumi_in in th_in]
        th_outs = [temphumi_out for temphumi_out in th_out]
        result = {'th_ins':th_ints, 'th_outs':th_outs}
        return result 

    def pressure_graph_data(self, **q):
        '''
        Function to get pressure readings from InfluxDB.

            time, Pressure

        '''
        query = "SELECT mean(pressure) AS pressure, "\
              + "mean(temperature) as Tout "\
              + "WHERE type = 'raw' OR type='external' "\
              + "AND time > NOW() - {} "\
              + "AND time < NOW() - {} "\
              
                             q['end'],
        press_raws = db_results.get_points(measurement='temphumi',
                                          tags={'type': 'raw'})
        Touts = db_results.get_points(measurement='temphumi',
                                          tags={'type': 'external'})                                  
        result = {'press_raws': press_raws, 'Touts': Touts}

import os

import index
from modules import voltage
from modules import weather
from modules import dynamic
from modules import status
from modules import temphumi
import config

def main_server_loop():
    ''' Master http server - the main executable / daemon
    
    Contains basic server settings and how the sub-modules
    are called and mounted to their respective paths

    Args:
        *None*

    Sets:
        *server_config:*    dict(), updates cherrypy.config
        *conf:*             dict(), see Cherrypy docs for more
        *cherrypy.config:*  dict(), see Cherrypy docs for more
        
    Returns:
        *N/A*

    Raises:
        *Exception*         If server is unable to start
    
    '''
    server_config={
        'server.socket_host': config.Conf.val['_server_bind_ip'],
        'server.socket_port': config.Conf.val['_server_port']
    }
    cherrypy.config.update(server_config)
    conf = {
        '/': {
            'tools.sessions.on': True,
            'tools.staticdir.root': os.path.abspath(config.SCRIPT_PATH + '/')
        },
        '/static': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': './static'
        }
    }

    cherrypy.tree.mount(voltage.EnergyInfo(), "/", conf)
    cherrypy.tree.mount(voltage.EnergyInfo(), "/energy", conf)
    cherrypy.tree.mount(weather.WeatherInfo(), "/weather", conf)
    cherrypy.tree.mount(status.StatusInfo(), "/status", conf)
    cherrypy.tree.mount(dynamic.Expose(), "/data", conf)
    cherrypy.tree.mount(temphumi.PuerhInfo(), "/temphumi", conf)
    cherrypy.engine.start()
    cherrypy.engine.block()

if __name__ == '__main__':
    try:
        main_server_loop()
    except Exception as e:
        raise e

static_dir = '/templates/' # Needs to have trailing and leading slash '/'

class landingPage(object):
    '''Base Index constructor and expose function'''
        header = config.read_html('header', static_dir)
        menu_raw = config.read_html('top_menu', static_dir)
        menu = menu_raw.format(energy = '', weather = '',
                               status = '', temphumi = '')
        body = self.body()
        footer = config.read_html('footer', static_dir)
        result = header\
               + menu\
               + body\
               + footer

    def body(self):
        admin_preformat = config.read_html('landing_page', static_dir)
        admin_html = admin_preformat
        return admin_html

                   "start",
                   "end",
                   "type"]

    Args:
        str(arguments):
        "A string of options to be directly evaluated as a dictionary"

    Returns:
        list(str(variable)):
        "List of strings, occupied by variables validated to be safe"

    '''

# Classes

        '''
        pass

        readable %

        Args:

        Returns:
        '''

        InfluxDB
        '''
        self.influx_voltage_client = config.Conf.influx_voltage_client

            returns:
        '''
        voltage_battery = self.influx_voltage_client.query(bat_query)

        state_db_result = self.influx_voltage_client.query(state_query)
        
        db_result = self.influx_voltage_client.query(query)

        result = {}
            48)

        '''
        result = []
        measure = self.influx_voltage_client.query(query)
        tm = []
        P_max = []
        Wh_sol = []

            row = []

        '''

        '''
                                           q['end'],
                             q['end'],
                                               q['end'],
        bat_measures = self.influx_voltage_client.query(bat_volt_query)
        measures = self.influx_voltage_client.query(query)
        mppt_measures = self.influx_voltage_client.query(mppt_query)
            try:
            except:

        '''
        
        else:
        
                tm,
                Bin,
                Bout,
                Din,
                Dout,
                Ein,
                Eout
            )

        )
            try:
            except:
                
        self.influx_status_client = config.Conf.influx_status_client
        
        '''
        '''
                             q['end'],

        '''
        '''
                             q['end'],

        '''
        '''
                             q['end'],

    '''
        self.influx_iot_client = config.Conf.influx_iot_client

        '''
        '''
                             q['end'],

        '''
        '''
                             q['end'],

        self.influx_weather_client = config.Conf.influx_weather_client
        
        '''
                             q['end'],

        '''
                             q['end'],

        '''

        '''
              
                             q['end'],

'''
Generic, system-wide variables and functions to be used in any / every module
Global variables are defined with _variable_name schema, to be quickly
identified in the project. 
'''
import os
SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
_templates = '/templates/'

# Functions
def read_html(filename, _STATIC_DIR):
    '''Read a html file

    Reads a file from a selected static directory - needs to be set as static
    in the cherrypy (chttpd.py).

    Args:
        ``filename`` *str()*, plain filename, without any path specification,
        without extension 
        ``_STATIC_DIR`` *str()*, path relative to the project root,
        where chttpd.py resides

    Returns:
        *str()*, parsed html code from the read file, or a HTML
        formatted error if file cannot be read for any reason

    Exceptions:
        On file read fail, string with Exception text is returned
        
    '''
    read_path = SCRIPT_PATH + _STATIC_DIR + filename + '.html'
    try:
        with open(read_path, 'r') as handle:
            return handle.read()
        return """<div>ERROR: {}!</div><br>{}""".format(e, read_path)

#Classes
class serverConfiguration(object):
    '''Sets up Conf with appropriate values

    Creates an object that holds the configuration to the whole web server,
    is available throughout the project. This separates the .ini style config
    and the config.py script, that uses additional logic.
    '''
        '''Init Conf values

        Sets up all values in config.Conf.val['option']=value

        Args:
            N/A

        Returns:
            N/A

        Sets:
            dict(self.val[option]): Set via read_config() method,
            does some additional calculations and parsing.
        '''
        if self.read_config('plutonium'):
            print("INFO: Config read success")
            print(self.val)
            self.val['_server_uri'] = \
                "{}://{}".format(
                    self.val['_server_protocol'],
                    self.val['_server_name']
                )
            self.val['_server_port'] = int(self.val['_server_port'])
            self.val['_influx_port'] = int(self.val['_influx_port'])
        else:
            print("ERROR: In reading config")

        self.influx_connectors()

    def influx_connectors(self):
        '''Set up client objects for InfluxDB

        All DB connector objects in one place. Callable from other modules

        Args:
            None

        Sets:
            ``self.influx_weather_client``,
            ``self.influx_voltage_client``,
            ``self.influx_iot_client``,
            ``self.influx_status_client``: Influx client connector objects
            
        Returns:
            N/A
            
        '''
        
            self.val['_influx_host'],
            self.val['_influx_port'],
            self.val['_influx_user'],
            self.val['_influx_pwd'],
            self.val['_influx_weather_db']
        )
        self.influx_voltage_client = influxdb.client.InfluxDBClient(
            self.val['_influx_voltage_db']
        )
            self.val['_influx_IoT_db']
        )
            self.val['_influx_status_db']
        )
            
    def read_config(self, conf_filename):
        '''Reads configuration file
    
        Read and parse the configuration options into a dictionary
        Why not using configparser? No idea, subject to change.
        This method is called on class init. 
    
        Args:
            ``conf_filename``, *str()* file name without the .ini extension,
            residing in ./config directory
    
        Returns:
            *dict()*, On success
            
            *bool(False)*, On failure

        Sets:
            ``self.dict()`` of name_value_pairs read from the config file
        
        '''
        read_path = SCRIPT_PATH + '/config/' + conf_filename + '.ini'
        try:
            with open(read_path, 'r') as conf_file:
                config_list = conf_file.readlines()
        except OSError as e:
            print("ERROR: {}".format(e))
            return False
        self.val = {}
        for line in config_list:
            if line.strip() != '':
                try:
                    line = line.split("=")
                    option = line[0].strip().strip("'")
                    value = line[1].strip().strip("'")
                except:
                    exit("WARNING: Wrong format of config option")
                    return False
                self.val.update({option:value})
            else:
                pass
        return self.val 

Conf = serverConfiguration()

#_server_protocol = "https"
#_server_name = "bastart.spoton.cz"
#_server_uri = "{}://{}".format(_server_protocol, _server_name)
#_server_port = 443
#_server_bind_ip = '0.0.0.0'
#
#_influx_host = 'localhost'
#_influx_port = 8086
#_influx_user = 'pi'
#_influx_pwd = 'Ventil6996'
#_influx_weather_db = 'weather_v2'
#_influx_status_db = 'status'
#_influx_voltage_db = 'voltage'
#_influx_IoT_db = 'weather_v2'