498 lines
21 KiB
Python
Executable File
498 lines
21 KiB
Python
Executable File
#!/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:
|
|
solar_value = ''
|
|
try:
|
|
mem_value = float(str(mem["Mem"]).strip())
|
|
except:
|
|
cap_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
|