diff --git a/Dockerfile b/Dockerfile index e22485e..9cd499b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,7 @@ COPY GivEnergy-Smart-Home-Display-givtcp/ ./GivEnergy-Smart-Home-Display-givtcp COPY givenergy_modbus/ /usr/local/lib/python3.10/site-packages/givenergy_modbus COPY startup.py startup.py -COPY startup_3.py startup_3.py +#COPY startup_3.py startup_3.py COPY redis.conf redis.conf COPY settings.json /app/settings.json @@ -108,4 +108,4 @@ ENV EVC_SELF_RUN_TIMER=5 EXPOSE 6345 1883 3000 5173 6379 9181 -CMD ["python3", "/app/startup_3.py"] +CMD ["python3", "/app/startup.py"] diff --git a/GivTCP/HA_Discovery.py b/GivTCP/HA_Discovery.py index 43b59fb..cb5a325 100644 --- a/GivTCP/HA_Discovery.py +++ b/GivTCP/HA_Discovery.py @@ -211,6 +211,9 @@ def create_device_payload(topic,SN): # If its a rate then change to Watts if "SOC" in str(topic).lower(): tempObj['unit_of_meas']="%" + tempObj['min']=4 + tempObj['max']=100 + tempObj['mode']="slider" elif "limit" in str(topic).lower(): #if EVC current tempObj['unit_of_meas']="A" tempObj['min']=6 @@ -221,6 +224,11 @@ def create_device_payload(topic,SN): tempObj['min']=0 tempObj['max']=HAMQTT.getinvbatmax() tempObj['mode']="slider" + elif "max_session_energy" in str(topic).lower(): + tempObj['unit_of_meas']="kWh" + tempObj['min']=0 + tempObj['max']=100 + tempObj['mode']="slider" else: tempObj['unit_of_meas']="%" elif GivLUT.entity_type[str(topic).split("/")[-1]].devType=="button": diff --git a/GivTCP/REST.py b/GivTCP/REST.py index 3cdc318..7a84813 100644 --- a/GivTCP/REST.py +++ b/GivTCP/REST.py @@ -40,6 +40,10 @@ def getAll(): def reboot(): return wr.rebootinverter() +@giv_api.route('/restart', methods=['GET']) +def restart(): + return wr.rebootAddon + #Publish last cached Invertor Data @giv_api.route('/readData', methods=['GET']) def rdData(): diff --git a/GivTCP/evc.py b/GivTCP/evc.py index 368741d..1f5d2bb 100644 --- a/GivTCP/evc.py +++ b/GivTCP/evc.py @@ -381,8 +381,8 @@ def hybridmode(): with open(GivLUT.regcache, 'rb') as inp: invRegCache= pickle.load(inp) sparePower=invRegCache[4]['Power']['Power']['PV_Power']-invRegCache[4]['Power']['Power']['Load_Power']+evcRegCache['Charger']['Active_Power_L1'] - spareCurrent=int(min(sparePower/invRegCache[4]['Power']['Power']['Grid_Voltage'],0)+6) #Spare current cannot be negative - if not spareCurrent==evcRegCache['Charger']['Current_L1']: + spareCurrent=int(max((sparePower/invRegCache[4]['Power']['Power']['Grid_Voltage']),0)+6) #Spare current cannot be negative + if not spareCurrent==evcRegCache['Charger']['Charge_Limit']: logger.info("Topping up min charge with Solar curent ("+str(spareCurrent-6)+"A), setting EVC charge to: "+str(spareCurrent)+"A") setCurrentLimit(spareCurrent) @@ -405,7 +405,7 @@ def solarmode(): setChargeControl("Start") setCurrentLimit(spareCurrent) else: - if not evcRegCache['Charger']['Control_Charge']=="Stop": + if not evcRegCache['Charger']['Charge_Control']=="Stop": logger.info("Solar excess dropped to below 6A, stopping charge") setChargeControl("Stop") @@ -420,7 +420,7 @@ def importcap(): evccurrent=float(evcRegCache['Charger']['Current_L1']) if importcurrent>GiV_Settings.evc_import_max_current: excess=importcurrent-GiV_Settings.evc_import_max_current - newcurrent=int(max(6,evccurrent-excess)) + newcurrent=int(max(6,evccurrent-excess)) #newcurrent must be at least 6A logger.info("Import current exceeded ("+str(excess)+"), reducing EVC charge to: "+str(newcurrent)+"A") setCurrentLimit(newcurrent) diff --git a/buildx.bat b/buildx.bat index 72c3d57..1599cc7 100644 --- a/buildx.bat +++ b/buildx.bat @@ -1,3 +1,3 @@ -docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -t britkat/giv_tcp-dev:2.3.104 -t britkat/giv_tcp-dev:latest --push . +docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -t britkat/giv_tcp-dev:2.3.112 -t britkat/giv_tcp-dev:latest --push . ::docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -t britkat/giv_tcp-dev:latest --push . ::docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -t britkat/giv_tcp-ma:latest -t britkat/giv_tcp-ma:2.3.3 --push . diff --git a/startup.py b/startup.py index 0baf92d..44b976f 100644 --- a/startup.py +++ b/startup.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from genericpath import exists import os, pickle, subprocess, logging,shutil, shlex, schedule from time import sleep @@ -7,8 +7,10 @@ import sys import requests from GivTCP.findInvertor import findInvertor +from GivTCP.findEVC import findEVC import givenergy_modbus.model.inverter from givenergy_modbus.client import GivEnergyClient +from pymodbus.client.sync import ModbusTcpClient selfRun={} mqttClient={} @@ -29,6 +31,18 @@ def palm_job(): subprocess.Popen(["/usr/local/bin/python3","/app/GivTCP_1/palm_soc.py"]) +def validateEVC(HOST): + logger.info("Validating "+str(HOST)) + try: + client = ModbusTcpClient(HOST) + regs = client.read_holding_registers(97,6).registers + systime=datetime(regs[0],regs[1],regs[2],regs[3],regs[4],regs[5]).replace(tzinfo=timezone.utc).isoformat() + return True + except: + e=sys.exc_info() + logger.info(e) + return False + def getInvDeets(HOST): try: client=GivEnergyClient(host=HOST) @@ -115,11 +129,32 @@ def getInvDeets(HOST): inverterStats={} invList={} list={} + evclist={} logger.critical("Scanning network for inverters...") try: for subnet in networks: if networks[subnet]: count=0 + # Get EVC Details + while len(evclist)<=0: + if count<2: + logger.info("EVC- Scanning network ("+str(count+1)+"):"+str(networks[subnet])) + evclist=findEVC(networks[subnet]) + if len(evclist)>0: break + count=count+1 + else: + break + if evclist: + poplist=[] + for evc in evclist: + if validateEVC(evclist[evc]): + logger.info("GivEVC found at: "+str(evclist[evc])) + else: + logger.info(evclist[evc]+" is not an EVC") + poplist.append(evc) + for pop in poplist: + evclist.pop(pop) #remove the unknown modbus device(s) + # Get Inverter Details while len(list)<=0: if count<2: logger.info("Scanning network ("+str(count+1)+"):"+str(networks[subnet])) @@ -129,7 +164,7 @@ def getInvDeets(HOST): else: break if list: - logger.info(str(len(list))+" Inverters found on "+str(networks[subnet])+" - "+str(list)) + logger.debug(str(len(list))+" Inverters found on "+str(networks[subnet])+" - "+str(list)) invList.update(list) for inv in invList: deets={} @@ -145,6 +180,8 @@ def getInvDeets(HOST): Stats['Model']=deets[2] Stats['Generation']=deets[1] inverterStats[inv]=Stats + else: + logger.error("Unable to interrogate inverter to get base details") count=count+1 else: break @@ -174,8 +211,8 @@ def getInvDeets(HOST): #rqdash=subprocess.Popen(["/usr/local/bin/rq-dashboard","-u redis://127.0.0.1:6379"]) #logger.critical("Running RQ Dashboard on port 9181") -vueConfig=subprocess.Popen(["npm", "run", "dev","-- --host"],cwd="/app/config_frontend") -logger.critical("Running Config Frontend") +#vueConfig=subprocess.Popen(["npm", "run", "dev","-- --host"],cwd="/app/config_frontend") +#logger.critical("Running Config Frontend") ########################################################################################################## # @@ -326,10 +363,15 @@ def getInvDeets(HOST): selfRun[inv]=subprocess.Popen(["/usr/local/bin/python3",PATH+"/read.py", "self_run2"]) if os.getenv('EVC_ENABLE')=="True" and inv==1: #only run it once - logger.critical ("Running EVC read loop every "+str(os.getenv('EVC_SELF_RUN_TIMER'))+"s") - evcSelfRun=subprocess.Popen(["/usr/local/bin/python3",PATH+"/evc.py", "self_run2"]) - logger.critical ("Subscribing MQTT Broker for EVC control") - mqttClientEVC=subprocess.Popen(["/usr/local/bin/python3",PATH+"/mqtt_client_evc.py"]) + if not os.getenv('EVC_IP_ADDRESS')=="": + logger.critical ("Running EVC read loop every "+str(os.getenv('EVC_SELF_RUN_TIMER'))+"s") + evcSelfRun=subprocess.Popen(["/usr/local/bin/python3",PATH+"/evc.py", "self_run2"]) + logger.critical ("Subscribing MQTT Broker for EVC control") + mqttClientEVC=subprocess.Popen(["/usr/local/bin/python3",PATH+"/mqtt_client_evc.py"]) + evcChargeModeLoop=subprocess.Popen(["/usr/local/bin/python3",PATH+"/evc.py", "chargeMode"]) + logger.critical ("Setting chargeMode loop to manage different charge modes every 60s") + else: + logger.critical("EVC IP is missing from config. Please update and restart GivTCP") if os.getenv('MQTT_OUTPUT')=="True" or isAddon: logger.critical ("Subscribing MQTT Broker for control") @@ -398,6 +440,16 @@ def getInvDeets(HOST): os.chdir(PATH) logger.critical ("Restarting EVC read loop every "+str(os.getenv('EVC_SELF_RUN_TIMER'))+"s") selfRun[inv]=subprocess.Popen(["/usr/local/bin/python3",PATH+"/evc.py", "self_run2"]) + if os.getenv('EVC_ENABLE')==True and not evcSelfRun.poll()==None: + logger.error("EVC Self Run loop process died. restarting...") + os.chdir(PATH) + logger.critical ("Restarting EVC read loop every "+str(os.getenv('EVC_SELF_RUN_TIMER'))+"s") + evcSelfRun=subprocess.Popen(["/usr/local/bin/python3",PATH+"/evc.py", "self_run2"]) + if os.getenv('EVC_ENABLE')==True and not evcChargeModeLoop.poll()==None: + logger.error("EVC Self Run loop process died. restarting...") + os.chdir(PATH) + logger.critical ("Restarting EVC chargeMode loop every 60s") + evcChargeModeLoop=subprocess.Popen(["/usr/local/bin/python3",PATH+"/evc.py", "chargeMode"]) #Run jobs for smart target schedule.run_pending() diff --git a/startup_3.py b/startup_3.py index 910d6f7..fa4080d 100644 --- a/startup_3.py +++ b/startup_3.py @@ -204,8 +204,8 @@ def getInvDeets(HOST): #rqdash=subprocess.Popen(["/usr/local/bin/rq-dashboard","-u redis://127.0.0.1:6379"]) #logger.critical("Running RQ Dashboard on port 9181") -#vueConfig=subprocess.Popen(["npm", "run", "dev","-- --host"],cwd="/app/config_frontend") -#logger.critical("Running Config Frontend") +vueConfig=subprocess.Popen(["npm", "run", "dev","-- --host"],cwd="/app/config_frontend") +logger.critical("Running Config Frontend") ########################################################################################################## #