#!/usr/bin/python3 import os import sys import cherrypy import influxdb import time import datetime import json 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 = 'freedavis' influx_db = 'voltage' influx_weather_db = 'weather' influx_status_db = 'status' variables_known = ["range", "granularity", "start", "end"] default_variables = {"range": "1h", "granularity": "30s", "end": "1s"} class DynamicData(object): def __init__(self): self.influx_client = influxdb.client.InfluxDBClient( influx_host, influx_port, influx_user, influx_pwd, influx_db ) self.influx_weather_client = influxdb.client.InfluxDBClient( influx_host, influx_port, influx_user, influx_pwd, influx_weather_db ) self.influx_status_client = influxdb.client.InfluxDBClient( influx_host, influx_port, influx_user, influx_pwd, influx_status_db ) @cherrypy.expose def index(self): return "Index, MOFO" def check_GET(self, arguments): ''' No other purpose than to make it more secure and only process the values that are meant to be processed. Hardcoded on top, mate! sets: list(): key values of those which should be processed, merged with deault values ''' self.q = eval(str(arguments)) keys_to_process = { key:self.q[key] for key in self.q.keys() if key in variables_known} resulting_variables = default_variables.copy() resulting_variables.update(keys_to_process) self.q = resulting_variables @cherrypy.expose def solar_monitor(self, **kwargs): ''' 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 ''' # GET variables now set, ready to reference them self.check_GET(kwargs) query1 = "SELECT mean(V_solar) as Usol, mean(I_solar) as Isol, " query2 = "mean(V_array) as Varr, mean(I_consumption) as Icons FROM voltage " query3 = "WHERE time > NOW() - {} AND time < NOW() - {} GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) query4 = "ORDER BY time DESC" query = "{} {} {} {}".format(query1, query2, query3, query4) measures = self.influx_client.query(query) # Let's get the data from DB header = "time,V_solar,I_solar,P_solar,P_cons\n" yield header for datapoint in measures["voltage"]: tm = str(datapoint["time"]).strip() solar_voltage = str(datapoint["Usol"]).strip() solar_current = str(datapoint["Isol"]).strip() array_voltage = str(datapoint["Varr"]).strip() consumption_current = str(datapoint["Icons"]).strip() if solar_voltage != 'None' and solar_current != 'None' \ and array_voltage != 'None' and consumption_current != 'None': solar_voltage = float(solar_voltage) / 1000.00 solar_current = float(solar_current) / 1000.00 array_voltage = float(array_voltage) / 1000.00 consumption_current = float(consumption_current) / 1000.00 else: solar_voltage = 0.00 solar_current = 0.00 array_voltage = 0.00 consumption_current = 0.00 # The 8W is the approximate internal consumption of the mppt controller ~ 0.15A # This value was removed, No idea why it appeared there in the first place. solar_power = round(solar_voltage * solar_current, 2) consumption_power = round(array_voltage * consumption_current, 2) yield "{},{},{},{},{}\n".format(tm, solar_voltage, solar_current, solar_power, consumption_power) @cherrypy.expose def wind_monitor(self, **kwargs): ''' Function to get wind value readings from InfluxDB. These parsed into a CSV yields: csv in raw, text format time, Speed, Gusts, Direction ''' # GET variables now set, ready to reference them self.check_GET(kwargs) speed_q1 = "SELECT mean(value) as w_speed FROM wind" gust_q1 = "SELECT mean(value) as w_gust FROM wind" direction_q1 = "SELECT mean(value) as w_dir FROM wind" speed_q2 = "WHERE time > NOW() - {} AND time < NOW() - {} AND type = 'speed' GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) gust_q2 = "WHERE time > NOW() - {} AND time < NOW() - {} AND type = 'windgust' GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) direction_q2 = "WHERE time > NOW() - {} AND time < NOW() - {} AND type = 'direction' GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) q3 = "ORDER BY time DESC" speed_query = "{} {} {}".format(speed_q1, speed_q2, q3) gust_query = "{} {} {}".format(gust_q1, gust_q2, q3) direction_query = "{} {} {}".format(direction_q1, direction_q2, q3) rs_speed = self.influx_weather_client.query(speed_query) rs_gust = self.influx_weather_client.query(gust_query) rs_direction = self.influx_weather_client.query(direction_query) # Let's get the data from DB header = "time,Speed,Gusts,Direction\n" yield header for speed, gust, direction in zip(rs_speed['wind'], rs_gust['wind'], rs_direction['wind']): tm_speed = str(speed["time"]).strip() tm_gust = str(gust["time"]).strip() tm_direction = str(direction["time"]).strip() speed_value = str(speed["w_speed"]).strip() gust_value = str(gust["w_gust"]).strip() direction_value = str(direction["w_dir"]).strip() #if tm_speed == tm_gust and tm_speed == tm_direction: #tm = strptime(speed["time"]).strip(), "%Y-%m-%dT%H:%M:%SZ") yield "{},{},{},{}\n".format(tm_speed, speed_value, gust_value, direction_value) @cherrypy.expose def temphumi_monitor(self, **kwargs): ''' Function to get temperature and humidity readings from InfluxDB. These parsed into a CSV yields: csv in raw, text format time, ''' # GET variables now set, ready to reference them self.check_GET(kwargs) temp_q1 = "SELECT mean(temperature) as temp FROM temphumi" hum_q1 = "SELECT mean(humidity) as hum FROM temphumi" in_q2 = "WHERE time > NOW() - {} AND time < NOW() - {} AND type = 'internal' GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) out_q2 = "WHERE time > NOW() - {} AND time < NOW() - {} AND type = 'external' GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) q3 = "ORDER BY time DESC" temp_in_query = "{} {} {}".format(temp_q1, in_q2, q3) temp_out_query = "{} {} {}".format(temp_q1, out_q2, q3) hum_in_query = "{} {} {}".format(hum_q1, in_q2, q3) hum_out_query = "{} {} {}".format(hum_q1, out_q2, q3) rs_temp_in = self.influx_weather_client.query(temp_in_query) rs_temp_out = self.influx_weather_client.query(temp_out_query) rs_hum_in = self.influx_weather_client.query(hum_in_query) rs_hum_out = self.influx_weather_client.query(hum_out_query) # Let's get the data from DB header = "time,T(ins),T(out),Humi(ins),Humi(out)\n" yield header for Tin, Tout, Hin, Hout in zip(rs_temp_in['temphumi'], rs_temp_out['temphumi'], rs_hum_in['temphumi'], rs_hum_out['temphumi']): tm_temp = str(Tin["time"]).strip() temp_in_val = str(Tin["temp"]).strip() temp_out_val = str(Tout["temp"]).strip() hum_in_val = str(Hin["hum"]).strip() hum_out_val = str(Hout["hum"]).strip() #if tm_speed == tm_gust and tm_speed == tm_direction: #tm = strptime(speed["time"]).strip(), "%Y-%m-%dT%H:%M:%SZ") yield "{},{},{},{},{}\n".format(tm_temp, temp_in_val, temp_out_val, hum_in_val, hum_out_val) @cherrypy.expose def pressure_monitor(self, **kwargs): ''' Function to get pressure readings from InfluxDB. These parsed into a CSV yields: csv in raw, text format time, Pressure ''' # GET variables now set, ready to reference them self.check_GET(kwargs) query1 = "SELECT mean(pressure) as pressure FROM temphumi" query2 = "WHERE type = 'adjusted' AND time > NOW() - {} AND time < NOW() - {} GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) query3 = "ORDER BY time DESC" query = "{} {} {}".format(query1, query2, query3) measures = self.influx_weather_client.query(query) # Let's get the data from DB header = "time,Pressure\n" yield header for datapoint in measures["temphumi"]: tm = str(datapoint["time"]).strip() pressure = str(datapoint["pressure"]).strip() yield "{},{}\n".format(tm, pressure) @cherrypy.expose def solcap_monitor(self, **kwargs): ''' Function to get solar and supercap readings from InfluxDB. These parsed into a CSV yields: csv in raw, text format time, Solar Irradiance, Capacitor ''' # GET variables now set, ready to reference them self.check_GET(kwargs) solar_query1 = "SELECT mean(voltage) as solar FROM iss" solar_query2 = "WHERE type = 'solar' AND time > NOW() - {} AND time < NOW() - {} GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) cap_query1 = "SELECT mean(voltage) as cap FROM iss" cap_query2 = "WHERE type = 'capcaitor' AND time > NOW() - {} AND time < NOW() - {} GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) query3 = "ORDER BY time DESC" query_solar = "{} {} {}".format(solar_query1, solar_query2, query3) query_cap = "{} {} {}".format(cap_query1, cap_query2, query3) measures_sol = self.influx_status_client.query(query_solar) measures_cap = self.influx_status_client.query(query_cap) # Let's get the data from DB header = "time,Solar,Capacitor\n" yield header for Sol, Cap in zip(measures_sol['iss'], measures_cap['iss']): tm = str(Sol["time"]).strip() try: solar_value = float(str(Sol["solar"]).strip()) / 100 except: solar_value = '' try: cap_value = float(str(Cap["cap"]).strip()) except: cap_value = '' yield "{},{},{}\n".format(tm, solar_value, cap_value) @cherrypy.expose def cpumem_monitor(self, **kwargs): ''' Function to get cpu,mem, disk % from InfluxDB. These parsed into a CSV yields: csv in raw, text format time, Cpu Mem, Disk ''' # GET variables now set, ready to reference them self.check_GET(kwargs) cpu_query1 = "SELECT mean(usage) as Cpu " mem_query1 = "SELECT mean(usage) as Mem " disk_query1 = "SELECT mean(usage) as Disk " query2 = "FROM RasPI " cpu_query3 = "WHERE type = 'cpu' AND time > NOW() - {} AND time < NOW() - {} GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) mem_query3 = "WHERE type = 'mem' AND time > NOW() - {} AND time < NOW() - {} GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) disk_query3 = "WHERE type = 'disk' AND time > NOW() - {} AND time < NOW() - {} GROUP BY time({})".format( self.q['range'], self.q['end'], self.q['granularity']) query4 = "ORDER BY time DESC" query_cpu = "{} {} {} {}".format(cpu_query1, query2, cpu_query3, query4) query_mem = "{} {} {} {}".format(mem_query1, query2, mem_query3, query4) query_disk = "{} {} {} {}".format(disk_query1, query2, disk_query3, query4) measures_cpu = self.influx_status_client.query(query_cpu) measures_mem = self.influx_status_client.query(query_mem) measures_disk = self.influx_status_client.query(query_disk) # Let's get the data from DB header = "time,Cpu,Mem,Disk\n" yield header for cpu, mem, disk in zip(measures_cpu['RasPI'], measures_mem['RasPI'], measures_disk['RasPI']): tm = str(cpu["time"]).strip() try: cpu_value = float(str(cpu["Cpu"]).strip()) except: cpu_value = '' try: mem_value = float(str(mem["Mem"]).strip()) except: mem_value = '' try: disk_value = float(str(disk["Disk"]).strip()) except: disk_value = '' yield "{},{},{},{}\n".format(tm, cpu_value, mem_value, disk_value) @cherrypy.expose def historical_values(self, **kwargs): ''' Function to all historical readings from InfluxDB. These parsed into a CSV? Dict? returns: csv in raw, text format time, V_solar, I_solar select mean(I_solar) as I_solar from voltage where time > now() - 10m group by time(30s) order by time desc ''' # GET variables now set, ready to reference them measure_range = kwargs['range'] query1 = "SELECT mean(V_array) as Uarr, mean(I_consumption) as Icon, " query2 = "mean(V_solar) as Usol, mean(I_solar) as Isol, " query3 = "mean(V_unit1) as Us1, mean(V_unit2) as Us2, max(charging) as charge FROM voltage" query4 = "WHERE time > NOW() - {} GROUP BY time(30s)".format(measure_range) query5 = "ORDER BY time DESC" query = "{} {} {} {} {}".format(query1, query2, query3, query4, query5) measures = self.influx_client.query(query) # Let's get the data from DB result = [] for datapoint in measures["voltage"]: tm = datapoint['time'] try: array_voltage = round(float(datapoint["Uarr"]), 2) current_consumed = round(float(datapoint["Icon"]) / array_voltage, 2) solar_voltage = round(float(datapoint["Usol"]), 2) solar_current = round(float(datapoint["Isol"]) / solar_voltage , 2) Us1 = round(float(datapoint["Us1"]), 2) Us2 = round(float(datapoint["Us2"]), 2) except: continue charging = int(datapoint["charge"]) row = {"time": tm, "V_array": array_voltage, "I_consumption": current_consumed, "V_solar": solar_voltage, "I_solar": solar_current, "V_unit1": Us1, "V_unit2": Us2, "charging": charging} result.append(row) return result @cherrypy.expose def stat_values(self, **kwargs): ''' Function to all historical readings from InfluxDB. These parsed into a CSV? Dict? returns: csv in raw, text format 24Wh_consumed, 24Wh_solar select mean(I_solar) as I_solar from voltage where time > now() - 10m group by time(30s) order by time desc ''' # GET variables now set, ready to reference them _days = kwargs['days'] d = datetime.datetime.now() measures = [] result = [] for t_range in range(1, _days*24, 24): day = (d - datetime.timedelta(hours = t_range)).strftime("%Y-%m-%d") query1 = "SELECT mean(V_array) as Uarr, mean(I_consumption) as Icon, " query2 = "mean(V_solar) as Usol, mean(I_solar) as Isol, " query3 = "mean(V_unit1) as Us1, mean(V_unit2) as Us2, max(charging) as charge FROM voltage" query4 = "WHERE time > '{} 00:00:00' AND time < '{} 23:59:59' GROUP BY time(1h) fill(0)".format(day, day) query5 = "ORDER BY time DESC" query = "{} {} {} {} {}".format(query1, query2, query3, query4, query5) measure = self.influx_client.query(query) # Let's get the data from DB tm = [] Ubat = [] Icon = [] Usol = [] Isol = [] Uss1 = [] Uss2 = [] P_cons = [] P_sol = [] charging = 0 day_result = [] for datapoint in measure["voltage"]: #print(datapoint) tstamp = datapoint['time'] array_voltage = round(float(datapoint["Uarr"]), 2) current_consumed = round(float(datapoint["Icon"]), 2) solar_voltage = round(float(datapoint["Usol"]), 2) solar_current = round(float(datapoint["Isol"]), 2) Us1 = round(float(datapoint["Us1"]), 2) Us2 = round(float(datapoint["Us2"]), 2) charge = int(datapoint["charge"]) p_consumed = round(float(array_voltage / 1000.00 \ * current_consumed / 1000.00), 2) p_solar = round(float(solar_voltage / 1000.00 \ * solar_current / 1000.00), 2) tm.append(tstamp), Ubat.append(array_voltage), Icon.append(current_consumed), Usol.append(solar_voltage), Isol.append(solar_current), Uss1.append(Us1), Uss2.append(Us2), charging = charging + charge P_cons.append(p_consumed) P_sol.append(p_solar) row = {"time": tm, "V_array": Ubat, "I_consumption": Icon, "V_solar": Usol, "I_solar": Isol, "V_unit1": Uss1, "V_unit2": Uss2, "charging": charging, "P_cons": P_cons, "P_sol": P_sol} try: day_result = [ row['time'][0][0:10], round(min(row['V_array']) / 1000, 2), round(sum(row['I_consumption']) / 1000, 2), round(max(row['V_solar']) / 1000, 2), round(sum(row['I_solar']) / 1000, 2), round(min(row['V_unit1']) / 1000, 2), round(min(row['V_unit2']) / 1000, 2), row['charging'], round(sum(row['P_cons']),2), round(sum(row['P_sol']), 2) ] except: day_result = [ d.strftime("%Y-%m-%d"), 0, 0, 0, 0, 0, 0, 0, 0, 0 ] # need to compute averages, mate... print(day_result) result.append(day_result) return result