diff --git a/web/chttpd.py b/web/chttpd.py
index 50a5645..d46d279 100755
--- a/web/chttpd.py
+++ b/web/chttpd.py
@@ -1,4 +1,6 @@
#!/usr/bin/python3
+"""Our cherrypy server, it all starts here
+"""
import os
import cherrypy
import OpenSSL
@@ -6,27 +8,37 @@ import sys
PATH = os.path.abspath(os.path.dirname(__file__))
+# Where are we?
_SCRIPT_PATH = os.path.dirname(sys.argv[0])
+# So we can load static libraries
sys.path.append(_SCRIPT_PATH)
+# Certificates reside here
+_CERT_PATH = _SCRIPT_PATH + '/.cert'
-import voltage
+"""Let's look at the statically linked stuff (i.e. the files that will
+serve us data). These imports are static files in the same directory. Each
+a spearate application with it's own face and behavior
+"""
+# UI for weather ./weather.py
import weather
+# CSV "API" for graphs, or individual pulling ./dynamic.py
import dynamic
+# UI for RasPi / davis status ./status.py
import status
+#basic config for the server, the SSL part is questionable...
server_config={
'server.socket_host': '0.0.0.0',
- 'server.socket_port':443,
- 'server.ssl_module':'builtin',
- 'server.ssl_certificate':'/home/www/.cert/fullchain1.pem',
- 'server.ssl_private_key':'/home/www/.cert/privkey1.pem'
+ 'server.socket_port': 443,
+ 'server.ssl_module': 'builtin',
+ 'server.ssl_certificate': _CERT_PATH + '/fullchain1.pem',
+ 'server.ssl_private_key': _CERT_PATH + '/privkey1.pem'
}
+# commit the config settings
cherrypy.config.update(server_config)
+# If launched directly, let's go
if __name__ == '__main__':
-
-
-
conf = {
'/': {
'tools.sessions.on': True,
@@ -42,9 +54,11 @@ if __name__ == '__main__':
}
}
- cherrypy.tree.mount(voltage.EnergyInfo(), "/", conf)
- cherrypy.tree.mount(weather.WeatherInfo(), "/weather", conf)
+ # Here are the different served mounts
+ cherrypy.tree.mount(weather.WeatherInfo(), "/", conf)
cherrypy.tree.mount(status.StatusInfo(), "/status", conf)
cherrypy.tree.mount(dynamic.DynamicData(), "/data", conf)
+
+ # Run the server, lock it in place.
cherrypy.engine.start()
cherrypy.engine.block()
diff --git a/web/readme.md b/web/readme.md
new file mode 100644
index 0000000..46cb9e7
--- /dev/null
+++ b/web/readme.md
@@ -0,0 +1,19 @@
+# The GUI. Web based. Python.
+This folder should contain all the files related to displaying and manipulating data via the UI.
+That means, everything related to the connections to the outside.
+
+# UPDATE: a complete rewrite of current to accommodate all new developers.
+By this I mean, we'll be slowly going through the whole code class by class to build a better world.
+
+## Server
+I'd say cherrypy. It is lightweight and relatively simple to use. Pure python, of course
+
+## DB
+Python calls to Influx. NOT using the influxdb library might be a good idea. We'll see.
+Currently the library is being used.
+
+## HTML
+Bootstrap 3, default admin / dasboard theme
+
+## JS graphs
+[Dygraphs](http://dygraphs.com/)
diff --git a/web/voltage.py b/web/voltage.py
deleted file mode 100755
index e6c66ea..0000000
--- a/web/voltage.py
+++ /dev/null
@@ -1,317 +0,0 @@
-#!/usr/bin/python3
-import os
-import sys
-import cherrypy
-import influxdb
-import time
-import datetime
-import json
-from dynamic import DynamicData
-
-# Universal variables
-_SCRIPT_PATH = os.path.dirname(sys.argv[0])
-_STATIC_DIR = '/templates/' # Needs to have trailing and leading slash '/'
-
-influx_host = 'localhost'
-influx_port = 8086
-influx_user = 'pi'
-influx_pwd = 'freedavis'
-influx_db = 'voltage'
-
-_MEASURES_IN_MINUTE = 11
-
-
-# Functions
-def read_html(filename):
- read_path = _SCRIPT_PATH + _STATIC_DIR + filename + '.html'
- try:
- with open(read_path, 'r') as handle:
- return handle.read()
- except:
- return """
ERROR!
""" + read_path
-
-# Set default structure
-
-html_start = ''
-body_start = ''
-body_close = ''
-html_close = ''
-
-class EnergyInfo(object):
- def __init__(self):
- self.influx_client = influxdb.client.InfluxDBClient(
- influx_host, influx_port, influx_user, influx_pwd, influx_db
- )
- self.array_full = '../static/img/battery_full.png' #100-90
- self.array_80 = '../static/img/battery_80.png' # 90-70
- self.array_60 = '../static/img/battery_60.png' # 70-50
- self.array_40 = '../static/img/battery_40.png' # 50-30
- self.array_20 = '../static/img/battery_20.png' # 30-10
- self.array_0 = '../static/img/battery_0.png' # 10-0
- self.array_ch = '../static/img/battery_charging.png'
- self.battery_icon = self.array_full
- self.measures_obj = DynamicData()
-
- def set_battery_icon(self, percentage, state):
- ''' Interprets % of battery state in icon changes
-
- expects:
- float(percentage): Percentage -> battery icon
-
- sets:
- str(self.battery_icon): the path to appropriate icon image
- '''
- if state == 1:
- self.battery_icon = self.array_ch
- else:
- if percentage > 70.0 and percentage < 89.9:
- self.battery_icon = self.array_80
- if percentage > 50.0 and percentage < 69.9:
- self.battery_icon = self.array_60
- if percentage > 30.0 and percentage < 49.9:
- self.battery_icon = self.array_40
- if percentage > 10.0 and percentage < 29.9:
- self.battery_icon = self.array_20
- if percentage > 0.0 and percentage < 9.9:
- self.battery_icon = self.array_0
-
- def percentageCalc(self, voltage, system):
- ''' Turns current charge for lead acid batteries into a human readable %
-
- expects:
- 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
- percentage = 100.00 if percentage > 100.00 else percentage
- return percentage
-
- @cherrypy.expose
- def index(self):
- header = read_html('header')
- menu_raw = read_html('top_menu')
- menu = menu_raw.format(energy = 'active', weather = '', status = '')
- body = self.body()
- footer = read_html('footer')
- result = header\
- + menu\
- + body\
- + footer
- return result
-
- def FreshValues(self):
- ''' Get most up-to-date energy reading.
-
- returns:
- dict(): {'I_solar': float(mA),
- 'V_unit2': float(mV),
- 'time': 'YYYY-mm-DDTHH:MM:SS.149636706Z',
- 'I_array': None, # ((deprecated, will be removed))
- 'V_unit1': float(mV),
- 'V_solar': float(mV),
- 'I_consumption': float(mA),
- 'V_array': float(mV),
- 'charging': bool(0/1)}
- '''
- query = "SELECT * FROM voltage ORDER BY time DESC LIMIT 1"
- last_measure = self.influx_client.query(query)
- charge_current = self.influx_client.query("select current from mppt where type = 'bat' order by time desc limit 1")
- pmax_day = self.influx_client.query("select Pmax from mppt where type = 'day' order by time desc limit 1")
- wh_day = self.influx_client.query("select Wh from mppt where type = 'day' order by time desc limit 1")
- power_solar = self.influx_client.query("select power from mppt where type = 'solar' order by time desc limit 1")
- state_r = self.influx_client.query("select * from mppt order by time desc limit 1")
- result = [measure for measure in last_measure["voltage"]][0]
- charge_current_result = [ch_curr for ch_curr in charge_current["mppt"]][0]["current"]
- pmax_day_result = [pmax for pmax in pmax_day["mppt"]][0]["Pmax"]
- wh_day_result = [wh for wh in wh_day["mppt"]][0]["Wh"]
- power_solar_result = [Psol for Psol in power_solar["mppt"]][0]["power"]
- state_result = [state for state in state_r["mppt"]][0]["state"]
- time_stamp = time.strptime(result['time'].split('.')[0],
- "%Y-%m-%dT%H:%M:%S")
- result['time'] = time_stamp
- result['state'] = state_result
- result['Psol'] = power_solar_result
- result['Wh_day'] = wh_day_result
- result['Pmax_day'] = pmax_day_result
- result['ChCurr'] = charge_current_result
- return result
-
-
- def HistoryValues(self, _INTERVAL):
- ''' Function to get energy readings from InfluxDB.
-
- returns:
- dict(): {"voltage_data": [{time: str(), V_array: str()...}, {...}],
- "voltage_data_cli": [str(line1), str(line2)...]}
- '''
-
- table_list = []
- query1 = "SELECT * FROM voltage"
- query2 = "ORDER BY time DESC".format(_INTERVAL)
- query = "{} {}".format(query1, query2)
- neasured_range = {'range': '1h'}
- measures = self.measures_obj.historical_values(range = '1h')
-
- # Let's get the data from DB
- for datapoint in measures:
- time_stamp = time.strptime(datapoint["time"].split('.')[0],
- "%Y-%m-%dT%H:%M:%SZ")
- array_volts = round(datapoint["V_array"] / 1000, 2)
- current_consumption = round(datapoint["I_consumption"] / 1000, 2)
- solar_volts = round(datapoint["V_solar"] / 1000, 2)
- current_solar = round(datapoint["I_solar"] / 1000, 2)
- unit1_volts = round(datapoint["V_unit1"] / 1000, 2)
- unit2_volts = round(datapoint["V_unit2"] / 1000, 2)
- charging = datapoint["charging"]
- power_consumption = round(current_consumption * 48, 2)
- power_solar = round(current_solar * solar_volts, 2)
- percent_array = self.percentageCalc(array_volts, 24)
- percent_unit1 = self.percentageCalc(unit1_volts, 12)
- percent_unit2 = self.percentageCalc(unit2_volts, 12)
- table_list.append([time.strftime("%Y/%m/%d %H:%M:%S", time_stamp),
- array_volts,
- current_consumption,
- solar_volts,
- current_solar,
- unit1_volts,
- unit2_volts,
- charging,
- power_consumption,
- power_solar])
- print(table_list)
- return {"voltage_data": table_list}
-
- def tableConstructor(self, header, body):
- ''' The idea behind is to have a method of constructing html
- element and to separate data and html code.
-
- expects:
- list(header): [[str(header1), str(header2), ...], row2, row3, ...]
- list(body): [str(td1), str(td2), ...]
-
- returns:
- str(html_table)
- '''
- table_header = ''
- table_body = ''
- table_begin = ''
- thead = ''
- thead_end = ''
- tbody = ''
- tbody_end = ''
- table_end = '
'
- table_header = ''
- for th in header:
- table_header = table_header + '' + th + ' | '
- table_header = table_header + ''
- for tr in body:
- table_body = table_body + ''
- for td in tr:
- table_body = table_body + '| ' + str(td) + ' | '
- table_body = table_body + '
'
-
- html_table = "{} {} {} {} {}".format(table_begin + thead,
- table_header,
- thead_end + tbody,
- table_body,
- tbody_end + table_end)
- return html_table
-
- def body(self):
- lastState = self.FreshValues()
- fresh_time = time.strftime("%Y-%m-%d %H:%M:%S", lastState['time'])
- try:
- fresh_voltage_array = round(float(lastState['V_array'] / 1000), 2)
- except:
- fresh_voltage_array = -99
- fresh_ch_current = lastState['ChCurr']
- try:
- fresh_voltage_solar = round(float(lastState['V_solar'] / 1000), 2)
- except:
- fresh_voltage_solar = -99
- fresh_current_solar = 10 #round(float(lastState['I_solar'] / 1000), 2)
- try:
- fresh_pmax_day = lastState['Pmax_day']
- except:
- fresh_pmax_day = -99
- fresh_voltage_unit2 = 10 #round(float(lastState['V_unit2'] / 1000), 2)
- fresh_percent_array = self.percentageCalc(fresh_voltage_array, 24)
- fresh_percent_unit2 = self.percentageCalc(fresh_voltage_unit2, 12)
- fresh_power_consumption = round(48\
- * fresh_ch_current, 2)
- fresh_power_solar = round(lastState['Psol'])
- fresh_charge_state = lastState['charging']
- fresh_table_header = ['Date Time',
- 'Uarr [V]',
- 'Icons [A]',
- 'Usol [V]',
- 'Isol [A]',
- 'Us1 [V]',
- 'Us2 [V]',
- 'Charge',
- 'Pcons [W]',
- 'Psol [W]']
-
- fresh_table_contents = [[fresh_time,
- fresh_voltage_array,
- fresh_ch_current,
- fresh_voltage_solar,
- fresh_current_solar,
- fresh_voltage_unit2,
- fresh_charge_state,
- fresh_power_consumption,
- fresh_power_solar]]
-
- fresh_table = self.tableConstructor(fresh_table_header,
- fresh_table_contents)
- fresh_table = ''
-
- #history_table_header = fresh_table_header
- #history_table_contents = self.HistoryValues(10)["voltage_data"]
- #history_table = self.tableConstructor(history_table_header,
- # history_table_contents)
-
- history_table = ''
-
- # Statistical table preparation
- stat_header = ['Date',
- 'Uarr min [V]',
- 'Icons [Ah]',
- 'Usol max [V]',
- 'Isol [Ah]',
- 'Us1 min [V]',
- 'Us2 min [V]',
- 'Charge [h]',
- 'Pcons [Wh]',
- 'Psol [Wh]']
-
- stats_content = self.measures_obj.stat_values(days = 7)
- stats_table = self.tableConstructor(stat_header, stats_content)
-
- #self.set_battery_icon(array_percent_adjusted, fresh_charge_state)
- admin_preformat = read_html('voltage_admin')
- admin_html = admin_preformat.format(array_voltage = fresh_voltage_array,
- charge_current = fresh_ch_current,
- solar_power = fresh_power_solar,
- voltage_solar = fresh_voltage_solar,
- pmax_day = fresh_pmax_day,
- array_voltage_icon = '../static/img/battery_full.png',
- array_percent = 0,
- unit1_percentage = 0,
- unit2_percentage = 0,
- fresh_table = fresh_table,
- history_table = history_table,
- stats_table = stats_table)
-
-
-
- return admin_html