#!/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