Files
SolarManager/solarManager.py
T
adminandClaude Opus 5 8c62455b2c Skoda: mehrere Schluessel, Wallbox-Ladeverlauf, Stecker als 1/0
- gatherSkodaData.py liest bis zu drei API-Schluessel und nutzt sie reihum;
  das Kontingent gilt gemessen fuers ganze Konto, daher feste Aufteilung
  14 Abruf / 4 Befehle / 2 Reserve
- nach einem Befehl aus der Weboberflaeche wird einmal ausser der Reihe
  nachgesehen
- Wallbox-Verlauf waehrend einer Ladung nach skoda_ladepunkte, dazu
  skoda_ladepunkte_nachtragen.py fuer Ladungen aus EnergyFlow
- evPlug ist jetzt ein Wahrheitswert: bool("no car") war immer True
- skoda.conf.example beschreibt das gemeinsame Format

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 09:29:57 +02:00

629 lines
27 KiB
Python

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
# carConnected ist ein Text ("no car", "charging", "ready",
# "complete"), kein Wahrheitswert - und bool("no car") ist True.
# So stand der Stecker der Wallbox im Carport dauerhaft auf
# gesteckt: in der Tabelle skoda, im Abruftakt von
# gatherSkodaData.py und als Steckersymbol im Realtime-SVG.
# "ready" heisst: Auto dran, wartet noch - also gesteckt.
# Als bool passt es zu evPlugOG und kommt per MQTT als 1/0 an.
rtData["evPlug"] = wp.carConnected not in (None, "no car")
# 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())