SolarManager unter Versionsverwaltung

Erster Stand der Hintergrundprozesse, die auf der Synology unter
/volume1/homes/wagner/SolarManager laufen: der Manager selbst, die Sammler
je Geraet, die MQTT-Bruecke, der Wecker und - neu hinzugezogen - der
AutoAction-Runner, der als Hintergrundprozess hierher gehoert und nicht ins
Web-Verzeichnis.

Zugangsdaten stehen nicht mehr im Quelltext, sondern in config.ini, die
nicht mit eingecheckt wird. Vorlage ist config.ini.example, gelesen wird sie
von konfig.py. Betroffen waren solarManager.py (Datenbank und Wattpilot),
zeit.py, gatherWaterData.py, wecker.py und skoda_testdaten.py, das sich das
Passwort bisher aus dem Quelltext eines anderen Moduls herausgesucht hat.

Die Kia-Anbindung ist mit dem Fahrzeug entfallen: kiaTest.py,
gatherCarData.py und hyundai_kia_connect_api sind nicht mehr dabei, ebenso
gatherInverterData.py, auf das nur noch eine auskommentierte Zeile zeigte.

Die mitgelieferten Bibliotheken bleiben im Repository - die NAS hat kein
pip, sie muessen neben den Skripten liegen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 20:46:59 +02:00
co-authored by Claude Opus 5
commit 79843aa2ae
968 changed files with 261182 additions and 0 deletions
+621
View File
@@ -0,0 +1,621 @@
import asyncio
from Wattpilot import Wattpilot
from dataclasses import dataclass
import sys
import logging
import gatherModbusData
import charger_goE
import webSocketServer
import time
from mysql.connector import connect, Error
import json
import gatherOpenDTUData
import gatherDTUBIData
import gatherSkodaData
import gatherWaterData
import gatherHeaterData
import gatherShellyEM3DataEG
import gatherShellyEM3DataUG
import datetime
from dateutil import tz
from suntime import Sun, SunTimeException
from typing import List
from dataclasses import dataclass
from dataclasses import field
from time import sleep
import dataclasses
import logging.config
import mqttClient
import konfig
logging.config.fileConfig('./logging.ini')
def is_summer_time(aware_dt):
assert aware_dt.tzinfo is not None
assert aware_dt.tzinfo.utcoffset(aware_dt) is not None
return bool(aware_dt.dst())
#Log_Format = "%(levelname)s %(asctime)s - %(message)s"
#logging.basicConfig(stream = sys.stdout,
# filemode = "w",
# format = Log_Format,
# level = logging.INFO)
#_LOGGER = logging.getLogger()
#Log_Format = "%(levelname)s %(module)s:%(lineno)d %(asctime)s - %(message)s"
#logging.basicConfig(stream=sys.stdout, format = Log_Format, level=logging.INFO)
_LOGGER = logging.getLogger()
#_LOGGER.setLevel(logging.INFO)
#logging.getLogger("Wattpilot").setLevel(logging.WARNING)
#handler = logging.StreamHandler(sys.stdout)
#handler.setLevel(logging.WARNING)
#formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
#handler.setFormatter(formatter)
#_LOGGER.addHandler(handler)
class EnhancedJSONEncoder(json.JSONEncoder):
def default(self, o):
if dataclasses.is_dataclass(o):
return dataclasses.asdict(o)
return super().default(o)
#"inverter1":"Symo Gen24 10.0",
#"inverter1nPV":2,
#"inverter2":"HM1500",
#"inverter2nPV":4,
#"inverter3":"HMT1800",
#"inverter3nPV":6,
#"inverter4":"HMs800w",
#"inverter4nPV":2,
@dataclass
class InvData:
p_PV:List[float] = field(default_factory=list)
p_AC:float = 0.0
temp:float = 0.0
error:int = 0
name:str = ""
rtData = {"P_Load":0.0,
"SOC":0.0,
"P_Akku":0.0,
"crgMaxPct":0.0,
"eff":0.0,
"P_PVn":[0.0]*34,
"P_PV":0.0,
"P_PV_est":0.0,
"invErr":0,
"autarky":0.0,
"P_Grid":0.0,
"P_WR":0.0,
"P_GridCons":0.0,
"P_GridFeed":0.0,
"ibatt":0.0,
"ubatt":0.0,
"tbatt":0.0,
"p_l1evu":0.0,
"p_l2evu":0.0,
"p_l3evu":0.0,
"p_l1og":0.0,
"p_l2og":0.0,
"p_l3og":0.0,
"p_l1eg":0.0,
"p_l2eg":0.0,
"p_l3eg":0.0,
"p_l1ug":0.0,
"p_l2ug":0.0,
"p_l3ug":0.0,
"og":0.0,
"eg":0.0,
"ug":0.0,
"i_l1evu":0.0,
"i_l2evu":0.0,
"i_l3evu":0.0,
"i_l1og":0.0,
"i_l2og":0.0,
"i_l3og":0.0,
"p_l1ev":0.0,
"p_l2ev":0.0,
"p_l3ev":0.0,
"evPower":0.0,
"evSOC":0.0,
"evLock":False,
"evFuel":0.0,
"evMode":"man",
"evRemChrgTime":0.0,
"evRange":0.0,
"evChgState":"",
"wbWh":0,
"wbogWh":0,
"evModeOG":"man",
"p_l1evOG":0.0,
"p_l2evOG":0.0,
"p_l3evOG":0.0,
"evPowerOG":0.0,
"evPlugOG":False,
"shellyUG":False,
"shellyOG":False,
"evPlug":False,
"t_buffT":0.0,
"t_buffM":0.0,
"t_buffB":0.0,
"t_heatVL":0.0,
"t_heatRL":0.0,
"t_gasVLu":0.0,
"t_gasVLo":0.0,
"t_gasRL":0.0,
"t_fbVL":0.0,
"t_fbRL":0.0,
"t_triac":0.0,
"pHeat":0.0,
"heatMode":"man",
"waterHeight":0,
"waterTemp": 0.0,
"inverters":[InvData(),InvData(),InvData(),InvData(),InvData(),InvData(),InvData(),InvData()],
"P_AC": 0.0
}
avgData = dict(rtData)
lock = asyncio.Lock()
def wp_handle_events(event, *args):
_LOGGER.debug(f"wp_handle_events(event={event},{args})")
_LOGGER.debug(f"wp_handle_events(): MQTT client not yet initialized - status publishing skipped.")
if event['type'] == 'fullStatus':
_LOGGER.debug(f"wp_handle_events()")
return
def isfloat(num):
try:
float(num)
return True
except ValueError:
return False
async def repeat(interval):
"""Run func every interval seconds.
If func has not finished before *interval*, will run again
immediately when the previous iteration finished.
*args and **kwargs are passed as the arguments to func.
"""
# Zugangsdaten stehen in config.ini und nicht mehr hier, siehe konfig.py.
_zugang = konfig.datenbank()
mysqlHost = _zugang["host"]
mysqlPort = _zugang["port"]
mysqlUser = _zugang["user"]
mysqlPW = _zugang["password"]
mysqlDB = _zugang["database"]
gatherSkodaData.setDbPasswort(mysqlPW)
avgCounter = 0
invErrAcc = 0
next_dbEntry = round(time.time()) + 300
_LOGGER.info("Manager started!")
lat = 47.5779944
lon = 10.2623373
sun = Sun(lat, lon)
lastDay = datetime.datetime.now()
query = "SELECT date FROM daylight ORDER BY date DESC LIMIT 1;"
lastSunriseDate = 0
try:
with connect(
host=mysqlHost,
port=mysqlPort,
user=mysqlUser,
password=mysqlPW,
database=mysqlDB,
) as connection:
with connection.cursor() as cursor:
cursor.execute(query)
myresult = cursor.fetchone()
connection.commit()
if myresult:
lastSunriseDate = myresult[0]
if(lastSunriseDate):
newDay = lastSunriseDate
while newDay < datetime.datetime.now().date() + datetime.timedelta(days=2):
sunrise = sun.get_sunrise_time(newDay,tz.gettz('Europe/Berlin')) #tz.gettz('Europe/Kaliningrad') tz.gettz('Europe/Berlin')
sunset = sun.get_sunset_time(newDay,tz.gettz('Europe/Berlin'))
if(is_summer_time(sunrise) == False):
sunrise = sunrise + datetime.timedelta(hours=1)
#_LOGGER.info("Wintertime!!")
if(is_summer_time(sunset) == False):
sunset = sunset + datetime.timedelta(hours=1)
#_LOGGER.info(str(sunrise.strftime('%H:%M:%S')))
query = "REPLACE INTO daylight (date, sunrise, sunset) VALUES ('"+newDay.isoformat()+"', '"+str(sunrise.strftime('%H:%M:%S'))+"', '"+str(sunset.strftime('%H:%M:%S'))+"');"
with connection.cursor() as cursor:
cursor.execute(query)
connection.commit()
newDay = newDay + datetime.timedelta(days=1)
except Error as e:
print(e)
sunrise = sun.get_sunrise_time(datetime.datetime.now(),tz.gettz('Europe/Berlin')) #tz.gettz('Europe/Kaliningrad') tz.gettz('Europe/Berlin')
sunset = sun.get_sunset_time(datetime.datetime.now(),tz.gettz('Europe/Berlin'))
if(is_summer_time(sunrise) == False):
sunrise = sunrise + datetime.timedelta(hours=1)
#_LOGGER.info("Wintertime!!")
if(is_summer_time(sunset) == False):
sunset = sunset + datetime.timedelta(hours=1)
wp = Wattpilot.Wattpilot(konfig.wert("wattpilot", "host"),
konfig.wert("wattpilot", "password"))
wp._auto_reconnect = 1
wp._reconnect_interval = 60
wp.connect()
wp.add_event_handler("ws_close", wp_handle_events)
wp.add_event_handler("ws_open", wp_handle_events)
wp.add_event_handler("wp_fullStatus_finished", wp_handle_events)
i=0
estProduction = 0.0
while (wp.allPropsInitialized == 0 and i < 10):
sleep(1);
i = i + 1;
water=await gatherWaterData.getWaterData()
rtData["waterHeight"] = water.waterHeight
rtData["waterTemp"] = water.temp
await gatherModbusData.connect()
mqttClient.startMqttClient()
rtData["inverters"][0].p_PV.append(0)
rtData["inverters"][0].p_PV.append(0)
while True:
inv,dtubi,dtu,heat,chrg,pEG,pUG,skoda,slp = await asyncio.gather(
gatherModbusData.get_runtime_data(estProduction, sunset),
gatherDTUBIData.gatherData(),
gatherOpenDTUData.gatherData(),
gatherHeaterData.gatherData(),
charger_goE.gatherNeededStatus(),
gatherShellyEM3DataEG.gatherData(),
gatherShellyEM3DataUG.gatherData(),
gatherSkodaData.gatherData(rtData["evPower"], rtData["evPlug"],
rtData["evPowerOG"], rtData["evPlugOG"],
rtData["P_PV"]/1000.0, rtData["P_Grid"]/1000.0,
rtData["wbWh"], rtData["wbogWh"]),
asyncio.sleep(interval),
)
rtData["evSOC"] = skoda.soc
rtData["evRange"] = skoda.range_km
rtData["evRemChrgTime"] = skoda.chgRemMin
rtData["evChgState"] = skoda.chgState
rtData["evFuel"] = skoda.fuelPct
if skoda.locked is not None:
rtData["evLock"] = skoda.locked
pvIt = 0
if inv is None:
_LOGGER.error("no inverter data available, skipping PV calculation fot Fronius!")
else:
for ppv in inv.p_PV:
if ppv is not None:
if ppv < 1:
ppv = 0
rtData["P_PVn"][pvIt] = round(ppv,2)
pvIt =pvIt+1
pvIt = 2
for ppv in dtubi.inverter[0].p_PV: #Veranda UG2
if ppv < 1:
ppv = 0
rtData["P_PVn"][pvIt] = round(ppv,2)
pvIt =pvIt+1
pvIt = 4
for ppv in dtu.inverter[0].p_PV: #Veranda UG/OG1
if ppv < 1:
ppv = 0
rtData["P_PVn"][pvIt] = round(ppv,2)
pvIt =pvIt+1
pvIt = 8
for ppv in dtu.inverter[3].p_PV: #was inverter 3 Veranda OG2-4
if ppv < 1:
ppv = 0
rtData["P_PVn"][pvIt] = round(ppv,2)
pvIt =pvIt+1
pvIt = 14
for ppv in dtu.inverter[2].p_PV: #carport 1 (HMS2250-6T)
if ppv < 1:
ppv = 0
rtData["P_PVn"][pvIt] = round(ppv,2)
pvIt =pvIt+1
pvIt = 20
for ppv in dtu.inverter[1].p_PV: #carport 2 (HMS2250-6T)
if ppv < 1:
ppv = 0
rtData["P_PVn"][pvIt] = round(ppv,2)
pvIt =pvIt+1
pvIt = 26
for ppv in dtu.inverter[4].p_PV: #carport 3 (HMS1800-4T)
if ppv < 1:
ppv = 0
rtData["P_PVn"][pvIt] = round(ppv,2)
pvIt =pvIt+1
pvIt = 30
for ppv in dtu.inverter[5].p_PV: #carport 4 (HMS1600-4T) only take three plates
if ppv < 1:
ppv = 0
if pvIt < 34:
rtData["P_PVn"][pvIt] = round(ppv,2)
pvIt =pvIt+1
invNum = 0
if inv is None:
pass
else:
rtData["inverters"][invNum].p_PV[0] = (inv.ppv)
rtData["inverters"][invNum].p_PV[1] = (inv.ppv2)
rtData["inverters"][invNum].p_AC = inv.p_AC
rtData["inverters"][invNum].temp = inv.temp
rtData["inverters"][invNum].name = "Gen24 "+str(inv.temp)
invNum = invNum+1
rtData["inverters"][invNum] = dtubi.inverter[0] #Veranda UG2
invNum = invNum+1
rtData["inverters"][invNum] = dtu.inverter[0] #Veranda UG/OG1
invNum = invNum+1
rtData["inverters"][invNum] = dtu.inverter[3] #Veranda OG2-4
invNum = invNum+1
rtData["inverters"][invNum] = dtu.inverter[2] #carport 1-6
invNum = invNum+1
rtData["inverters"][invNum] = dtu.inverter[1] #carport 7-12
invNum = invNum+1
rtData["inverters"][invNum] = dtu.inverter[4] #carport 13-16
invNum = invNum+1
rtData["inverters"][invNum] = dtu.inverter[5] #carport 17-19
invNum = invNum+1
# Wer hat in diesem Durchlauf keine frischen Daten geliefert? Ein Bit je
# Wechselrichter in der Reihenfolge oben, damit die Zeile spaeter selbst
# sagt, ob pvP eine Messung ist. P_PV_est haelt fest, wieviel davon aus
# den gesunden Nachbarn hochgerechnet wurde - pvP minus pvP_est ist
# also weiterhin die reine Messung.
if inv is None:
rtData["inverters"][0].error += 1
else:
rtData["inverters"][0].error = 0
rtData["invErr"] = 0
estSum = 0.0
for wrNum, wr in enumerate(rtData["inverters"]):
if wr.error:
rtData["invErr"] |= (1 << wrNum)
estSum += getattr(wr, "p_PV_est", 0.0)
rtData["P_PV_est"] = round(estSum,2)
try:
rtData["P_AC"] = 0#rtData["inverters"][0].p_AC#inv.p_AC
for inverter in rtData["inverters"]:
rtData["P_AC"] = rtData["P_AC"] + inverter.p_AC
rtData["P_PV"] = round(sum(rtData["P_PVn"]),2)
rtData["P_Grid"] = inv.pgrid
if inv.pgrid > 0:
rtData["P_GridCons"] = inv.pgrid
rtData["P_GridFeed"] = 0
else:
rtData["P_GridCons"] = 0
rtData["P_GridFeed"] = -inv.pgrid
rtData["P_WR"] = inv.p_wr
rtData["ibatt"] = inv.ibatt
rtData["ubatt"] = inv.ubatt
rtData["tbatt"] = 0
rtData["crgMaxPct"] = inv.crgMaxPct
#rtData["pwrMaxPct"] = inv.pwrMaxPct
if((rtData["P_PV"]+rtData["P_Akku"]) != 0):
rtData["eff"] = ((100.0 / (rtData["P_PV"]+rtData["P_Akku"])) * rtData["P_AC"])
else:
rtData["eff"] = 0
rtData["p_l1evu"] = inv.p_l1evu
rtData["p_l2evu"] = inv.p_l2evu
rtData["p_l3evu"] = inv.p_l3evu
rtData["i_l1evu"] = inv.i_l1evu
rtData["i_l2evu"] = inv.i_l2evu
rtData["i_l3evu"] = inv.i_l3evu
rtData["p_l1og"] = inv.p_l1og
rtData["p_l2og"] = inv.p_l2og
rtData["p_l3og"] = inv.p_l3og
rtData["i_l1og"] = inv.i_l1og
rtData["i_l2og"] = inv.i_l2og
rtData["i_l3og"] = inv.i_l3og
rtData["og"] = inv.p_l1og + inv.p_l2og + inv.p_l3og - (chrg.p_l1ev+chrg.p_l2ev+chrg.p_l3ev)*1000
rtData["p_l1eg"] = pEG.P_L1
rtData["p_l2eg"] = pEG.P_L2
rtData["p_l3eg"] = pEG.P_L3
rtData["eg"] = pEG.P_L1 + pEG.P_L2 + pEG.P_L3 + (wp.power1+wp.power2+wp.power3)*1000
rtData["p_l1ug"] = pUG.P_L1
rtData["p_l2ug"] = pUG.P_L2
rtData["p_l3ug"] = pUG.P_L3
rtData["ug"] = pUG.P_L1 + pUG.P_L2 + pUG.P_L3
rtData["P_Akku"] = inv.pbat
rtData["SOC"] = inv.soc
rtData["evMode"] = wp.mode
rtData["p_l1ev"] = wp.power1
rtData["p_l2ev"] = wp.power2
rtData["p_l3ev"] = wp.power3
rtData["evPower"] = wp.power
rtData["evPlug"] = wp.carConnected
# Gesamtzaehler beider Wallboxen in Wh. Die Differenz zweier
# Staende trifft eine Ladung auf die Wattstunde genau - genauer
# als jede Summe ueber die Fuenf-Minuten-Leistungswerte, deren
# Fenstergrenzen nie auf Anfang und Ende der Ladung fallen.
rtData["wbWh"] = int(wp.energyCounterTotal or 0)
rtData["evModeOG"] = chrg.mode
rtData["p_l1evOG"] = chrg.p_l1ev
rtData["p_l2evOG"] = chrg.p_l2ev
rtData["p_l3evOG"] = chrg.p_l3ev
rtData["evPowerOG"] = chrg.p_l1ev + chrg.p_l2ev + chrg.p_l3ev
rtData["evPlugOG"] = chrg.connected
rtData["wbogWh"] = chrg.eto
rtData["heatMode"] = heat.mode
rtData["t_buffT"] = heat.t_buffT
rtData["t_buffM"] = heat.t_buffM
rtData["t_buffB"] = heat.t_buffB
rtData["t_heatVL"] = heat.t_heatVL
rtData["t_heatRL"] = heat.t_heatRL
rtData["t_gasVLu"] = heat.t_gasVLu
rtData["t_gasVLo"] = heat.t_gasVLo
rtData["t_gasRL"] = heat.t_gasRL
rtData["t_fbVL"] = heat.t_fbVL
rtData["t_fbRL"] = heat.t_fbRL
rtData["t_triac"] = heat.t_triac
rtData["pHeat"] = heat.p_heat
rtData["P_Load"] = -(rtData["P_AC"] + rtData["P_Grid"])# + rtData["P_Akku"])
if(-rtData["P_Load"] < -rtData["og"] + rtData["eg"] + rtData["ug"] + rtData["pHeat"] ): #if Pload is less than combined consumption, there is a measurement error-- assume Pload still a little more than consumption to leave some common consumption
rtData["P_Load"] = -(-rtData["og"] + rtData["eg"] + rtData["ug"] + rtData["pHeat"])-50
if rtData["P_Grid"] > 0 and rtData["P_Load"] < 0:
rtData["autarky"] = 100 + 100*(rtData["P_Grid"]/rtData["P_Load"]) #load is negative
if rtData["autarky"] > 100:
rtData["autarky"] = 100
if rtData["autarky"] < 0:
rtData["autarky"] = 0
else:
rtData["autarky"] = 100
try:
for key in avgData:
if isinstance(avgData[key],float):
avgData[key] += rtData[key]
invErrAcc |= rtData["invErr"] # Bits sammeln, nicht mitteln
avgCounter = avgCounter+1
except:
_LOGGER.error("data average failed!")
except Exception as e:
_LOGGER.error(e)
_LOGGER.error("rtdata fill failed!")
if round(time.time()) >= next_dbEntry:
next_dbEntry = next_dbEntry + 300
water=await gatherWaterData.getWaterData()
rtData["waterHeight"] = water.waterHeight
rtData["waterTemp"] = water.temp
if avgCounter:
for key in avgData:
if isinstance(avgData[key],float):
avgData[key] /= avgCounter
#_LOGGER.info("DB-OUTPUT!!")
if(-avgData["P_Load"] < (-avgData["p_l1og"] - avgData["p_l2og"] - avgData["p_l3og"] + avgData["p_l1ev"] + avgData["p_l2ev"] + avgData["p_l3ev"] + avgData["p_l1evOG"] + avgData["p_l2evOG"] + avgData["p_l3evOG"] + avgData["pHeat"])): #null heater consumption, if total consumption is too small so it would result in negative UG consumption
avgData["pHeat"] = -avgData["P_Load"] - (-avgData["p_l1og"] - avgData["p_l2og"] - avgData["p_l3og"]) - 300; #reduce heater consumption in case PPV power is not gathered correctly
if(-avgData["P_Load"] < (-avgData["p_l1og"] - avgData["p_l2og"] - avgData["p_l3og"] + avgData["p_l1ev"] + avgData["p_l2ev"] + avgData["p_l3ev"] + avgData["p_l1evOG"] + avgData["p_l2evOG"] + avgData["p_l3evOG"] + avgData["pHeat"])): #null car consumption, of total consumption is too small so it would result in negative UG consumption
avgData["p_l1ev"] = 0
avgData["p_l2ev"] = 0
avgData["p_l3ev"] = 0
avgData["p_l1evOG"] = 0
avgData["p_l2evOG"] = 0
avgData["p_l3evOG"] = 0
if(avgData["pHeat"] < 0): #when we reduced too much from heater, there must have been more PV power available (inverters didn't report production)
avgData["P_PV"] = avgData["P_PV"] - avgData["pHeat"]
avgData["pHeat"] = 0
if avgData["P_Load"] > 0 or -avgData["P_Load"] < -avgData["og"] + avgData["p_l2ev"] + avgData["p_l3ev"] + avgData["p_l1evOG"] + avgData["p_l2evOG"] + avgData["p_l3evOG"] + avgData["pHeat"]: #if load is not negative something is wrong
avgData["P_Load"] = -(avgData["P_PV"] + avgData["P_Akku"]) + 20 #try to recalculate real consumption from corrected PV production
if avgData["P_Load"] > 0 or -avgData["P_Load"] < -avgData["og"] + avgData["p_l2ev"] + avgData["p_l3ev"] + avgData["p_l1evOG"] + avgData["p_l2evOG"] + avgData["p_l3evOG"] + avgData["pHeat"]: #if the correction still is not matching assume zero consumption for UG for smallest error
avgData["P_Load"] = -(-avgData["og"] + avgData["p_l2ev"] + avgData["p_l3ev"] + avgData["p_l1evOG"] + avgData["p_l2evOG"] + avgData["p_l3evOG"] + avgData["pHeat"])
try:
with connect(
host=mysqlHost,
port=mysqlPort,
user=mysqlUser,
password=mysqlPW,
database=mysqlDB,
) as connection:
query = ("INSERT INTO EnergyFlow (datetime, "
"totalConsumption, soc, battP, "
"pvP, autonomy, gridP, "
"gridPcons, gridPfeed, "
"battI, battU, battTemp, "
"IL1_EVU, IL2_EVU, IL3_EVU, "
"IL1_OG, IL2_OG, IL3_OG, "
"PL1_EVU, PL2_EVU, PL3_EVU, "
"PL1_OG, PL2_OG, PL3_OG, "
"PL1_EG, PL2_EG, PL3_EG, "
"PL1_UG, PL2_UG, PL3_UG, "
"PL1_EV, PL2_EV, PL3_EV, "
"PL1_EVog, PL2_EVog, PL3_EVog, "
"HeizungOG, HeizungEG, heaterPwr, "
"invErr, pvP_est) VALUES ("
"NOW(),"
+str(avgData["P_Load"])+","+str(avgData["SOC"])+","+str(avgData["P_Akku"])+","
+str(avgData["P_PV"])+","+str(avgData["autarky"])+","+str(avgData["P_Grid"])+","
+str(avgData["P_GridCons"])+","+str(avgData["P_GridFeed"])+","
+str(avgData["ibatt"])+","+str(avgData["ubatt"])+","+str(avgData["tbatt"])+","
+str(avgData["i_l1evu"])+","+str(avgData["i_l2evu"])+","+str(avgData["i_l3evu"])+","
+str(avgData["i_l1og"])+","+str(avgData["i_l2og"])+","+str(avgData["i_l3og"])+","
+str(avgData["p_l1evu"])+","+str(avgData["p_l2evu"])+","+str(avgData["p_l3evu"])+","
+str(avgData["p_l1og"])+","+str(avgData["p_l2og"])+","+str(avgData["p_l3og"])+","
+str(avgData["p_l1eg"])+","+str(avgData["p_l2eg"])+","+str(avgData["p_l3eg"])+","
+str(avgData["p_l1ug"])+","+str(avgData["p_l2ug"])+","+str(avgData["p_l3ug"])+","
+str(avgData["p_l1ev"])+","+str(avgData["p_l2ev"])+","+str(avgData["p_l3ev"])+","
+str(avgData["p_l1evOG"])+","+str(avgData["p_l2evOG"])+","+str(avgData["p_l3evOG"])+","
+str(0)+","+str(0)+","+str(avgData["pHeat"])+","
+str(invErrAcc)+","+str(avgData["P_PV_est"])+");")
with connection.cursor() as cursor:
cursor.execute(query)
connection.commit()
newDay = datetime.datetime.now() + datetime.timedelta(hours=24-6) #run this at 6 o'clock
if(newDay.strftime('%d') != lastDay.strftime('%d')):
lastDay = newDay
sunrise = sun.get_sunrise_time(newDay,tz.gettz('Europe/Berlin')) #tz.gettz('Europe/Kaliningrad') tz.gettz('Europe/Berlin')
sunset = sun.get_sunset_time(newDay,tz.gettz('Europe/Berlin'))
if(is_summer_time(sunrise) == False):
sunrise = sunrise + datetime.timedelta(hours=1)
#_LOGGER.info("Wintertime!!")
if(is_summer_time(sunset) == False):
sunset = sunset + datetime.timedelta(hours=1)
#_LOGGER.info(str(sunrise.strftime('%H:%M:%S')))
query = "REPLACE INTO daylight (date, sunrise, sunset) VALUES (CURDATE()+1, '"+str(sunrise.strftime('%H:%M:%S'))+"', '"+str(sunset.strftime('%H:%M:%S'))+"');"
with connection.cursor() as cursor:
cursor.execute(query)
connection.commit()
query = "SELECT SUM(power)/2 AS estProd FROM simPower WHERE DATE(period_end) = DATE(NOW());"
with connection.cursor() as cursor:
cursor.execute(query)
myresult = cursor.fetchone()
connection.commit()
estProduction = myresult[0]
except Error as e:
_LOGGER.error(e)
for key in avgData:
if isinstance(avgData[key],float):
avgData[key] = 0.0
avgCounter = 0
invErrAcc = 0
#if(rtData["SOC"] > 70.0):
#await charger_goE.setPVparameters((rtData["P_Grid"]+rtData["P_Akku"])+(4000*(100-rtData["SOC"])/100),rtData["P_PV"]+rtData["P_PV2"]+rtData["P_PV3"],0)
#else:
#await charger_goE.setPVparameters(500,0,0)
#if(chrg.mode == "eco" and avgCounter == 5):
# await charger_goE.setChargePower(rtData["P_PV"],chrg.Phase_set_ev,chrg.Iset_ev,chrg.AllowCharging_ev)
#print(json.dumps(rtData))
publishData = json.dumps(rtData, cls=EnhancedJSONEncoder)
webSocketServer.message_all(publishData)
mqttClient.publish(rtData)
if(rtData["SOC"] > 70.0):
chrgCons = (rtData["P_Grid"]+rtData["P_Akku"])+(4000*(100-rtData["SOC"])/100)
pv_ges = rtData["P_PV"]
await charger_goE.setPVparameters(chrgCons,pv_ges,0)
#_LOGGER.error(f"goE Parameters Sent PV")
else:
await charger_goE.setPVparameters(500,0,0)
#_LOGGER.error(f"goE Parameters Sent")
async def main():
await webSocketServer.serveWebSocket()
await asyncio.gather(
repeat(3)
)
await webSocketServer.closeWebSocket()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())