diff --git a/.dockerignore b/.dockerignore index 8e1b7284..4257687e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ -src/settings.py \ No newline at end of file +src/settings.py +src/HA_Discovery.py \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0601d806..a2371c8e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ src/__pycache__ src/givtcp_debug.log .venv .vscode -buildx.bat \ No newline at end of file +buildx.bat +/givenergy_modbus diff --git a/Dockerfile b/Dockerfile index fc100683..7d7c4a44 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ RUN pip install -r requirements.txt COPY src/ . ENV INVERTOR_IP="" +ENV NUM_BATTERIES="1" ENV MQTT_OUTPUT="True" ENV MQTT_ADDRESS="127.0.0.1" ENV MQTT_USERNAME="" @@ -28,7 +29,7 @@ ENV LOG_LEVEL="Error" ENV DEBUG_FILE_LOCATION="" ENV PRINT_RAW="False" ENV SELF_RUN="True" -ENV SELF_RUN_LOOP_TIMER="10" +ENV SELF_RUN_LOOP_TIMER="20" ENV INFLUX_OUTPUT="False" ENV INFLUX_URL="" ENV INFLUX_TOKEN="" diff --git a/buildx.bat b/buildx.bat index 64769a6b..23c3d239 100644 --- a/buildx.bat +++ b/buildx.bat @@ -1,2 +1 @@ -git pull -docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -t britkat/giv_tcp-ma:latest --push . +docker buildx build --no-cache --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -t britkat/giv_tcp-ma:latest --push . diff --git a/givtcp.yaml b/givtcp.yaml index 6484da8f..1585945f 100644 --- a/givtcp.yaml +++ b/givtcp.yaml @@ -1,7 +1,7 @@ sensor: - platform: rest name: GivTCP - resource: http://192.168.2.10:6345/runAll #GivTCP Invertor connection + resource: http://192.168.2.10:6345/runAll #GivTCP docker container connection method: GET scan_interval: 20 # refresh timeout: 15 diff --git a/src/HA_Discovery.py b/src/HA_Discovery.py new file mode 100644 index 00000000..884c41ea --- /dev/null +++ b/src/HA_Discovery.py @@ -0,0 +1,114 @@ +# version 2022.01.21 +from array import array +from logging import Logger +import paho.mqtt.client as mqtt +import time +import datetime +import json + +import logging +from settings import GiV_Settings +from givenergy_modbus.model.inverter import Model +from mqtt import GivMQTT + +if GiV_Settings.Log_Level.lower()=="debug": + if GiV_Settings.Debug_File_Location=="": + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(filename=GiV_Settings.Debug_File_Location, encoding='utf-8', level=logging.DEBUG) +elif GiV_Settings.Log_Level.lower()=="info": + if GiV_Settings.Debug_File_Location=="": + logging.basicConfig(level=logging.INFO) + else: + logging.basicConfig(filename=GiV_Settings.Debug_File_Location, encoding='utf-8', level=logging.INFO) +else: + if GiV_Settings.Debug_File_Location=="": + logging.basicConfig(level=logging.ERROR) + else: + logging.basicConfig(filename=GiV_Settings.Debug_File_Location, encoding='utf-8', level=logging.ERROR) + +logger = logging.getLogger("GivTCP") + +class discovery_message(): + name: str + stat_t: str + avty_t: str + pl_avail: str + pl_not_avail: str + json_attr_t: str + unit_of_meas: str + ic: str + uniq_id: str + device_class: str + ids: str + mf: str + +class HAMQTT(): + + if GiV_Settings.MQTT_Port=='': + MQTT_Port=1883 + else: + MQTT_Port=int(GiV_Settings.MQTT_Port) + MQTT_Address=GiV_Settings.MQTT_Address + if GiV_Settings.MQTT_Username=='': + MQTTCredentials=False + else: + MQTTCredentials=True + MQTT_Username=GiV_Settings.MQTT_Username + MQTT_Password=GiV_Settings.MQTT_Password + if GiV_Settings.MQTT_Topic=="": + GiV_Settings.MQTT_Topic="GivEnergy" + + + def on_connect(client, userdata, flags, rc): + if rc==0: + client.connected_flag=True #set flag + logger.info("connected OK Returned code="+str(rc)) + #client.subscribe(topic) + else: + logger.info("Bad connection Returned code= "+str(rc)) + + def publish_discovery(array,SN): #Recieve multiple payloads with Topics and publish in a single MQTT connection + mqtt.Client.connected_flag=False #create flag in class + client=mqtt.Client("GivEnergy_GivTCP") + rootTopic=str(GiV_Settings.MQTT_Topic+"/"+SN+"/") + if HAMQTT.MQTTCredentials: + client.username_pw_set(HAMQTT.MQTT_Username,HAMQTT.MQTT_Password) + client.on_connect=HAMQTT.on_connect #bind call back function + client.loop_start() + logger.info ("Connecting to broker: "+ HAMQTT.MQTT_Address) + client.connect(HAMQTT.MQTT_Address,port=HAMQTT.MQTT_Port) + while not client.connected_flag: #wait in loop + logger.info ("In wait loop") + time.sleep(0.2) + ### For each topic create a discovery message + for p_load in array: + payload=array[p_load] + logger.info('Publishing: '+rootTopic+p_load) + output=GivMQTT.iterate_dict(payload,rootTopic+p_load) #create LUT for MQTT publishing + for topic in output: + client.publish("homeassistant/sensor/GivEnergy/"+str(topic).split("/")[-1]+"/config",HAMQTT.create_payload(topic)) + client.loop_stop() #Stop loop + client.disconnect() + return client + + def create_payload(topic): #Create LUT of topics and datapoints + tempObj={} + tempObj["xxxx"]=str(topic).split("/")[-1] #Just final bit past the last "/" + tempObj['avty_t'] = "Givenergy/status" + tempObj['json_attr_t']=str(topic) + tempObj['stat_t']=str(topic) + tempObj['uniq_id']=tempObj["xxxx"] + if "Energy" in str(topic): + tempObj['unit_of_meas']="kWh" + tempObj['device_class']="Energy" + elif "Power" in str(topic): + tempObj['unit_of_meas']="W" + tempObj['device_class']="Power" + tempObj['dev']={} + tempObj['dev']['ids']=str(tempObj["xxxx"]).replace(" ","_") + tempObj['dev']['mf']="GivEnergy" + ## Convert this object to json string + jsonOut=json.dumps(tempObj) + jsonOut=str(jsonOut).replace("xxxx","name") + return(jsonOut) \ No newline at end of file diff --git a/src/mqtt.py b/src/mqtt.py index 54d57998..b3392722 100644 --- a/src/mqtt.py +++ b/src/mqtt.py @@ -6,6 +6,7 @@ import logging from settings import GiV_Settings +#from HA_Discovery import HAMQTT from givenergy_modbus.model.inverter import Model if GiV_Settings.Log_Level.lower()=="debug": @@ -51,7 +52,9 @@ def on_connect(client, userdata, flags, rc): def multi_MQTT_publish(rootTopic,array): #Recieve multiple payloads with Topics and publish in a single MQTT connection mqtt.Client.connected_flag=False #create flag in class client=mqtt.Client("GivEnergy_GivTCP") - + + ##Check if first run then publish auto discovery message + if GivMQTT.MQTTCredentials: client.username_pw_set(GivMQTT.MQTT_Username,GivMQTT.MQTT_Password) client.on_connect=GivMQTT.on_connect #bind call back function diff --git a/src/read.py b/src/read.py index 3d8ccc4c..353867f0 100644 --- a/src/read.py +++ b/src/read.py @@ -5,6 +5,7 @@ import json import logging import datetime +from HA_Discovery import HAMQTT from settings import GiV_Settings from givenergy_modbus.client import GivEnergyClient from givenergy_modbus.model.inverter import Inverter, Model @@ -48,11 +49,12 @@ def runAll(): #Connect to Invertor and load data try: + # starttime=datetime.datetime.now() + # logger.error("Start time for library invertor call: "+ datetime.datetime.strftime(starttime,"%H:%M:%S")) client=GivEnergyClient(host=GiV_Settings.invertorIP) InvRegCache = RegisterCache() client.update_inverter_registers(InvRegCache) GEInv=Inverter.from_orm(InvRegCache) - numBatteries=1 try: numBatteries=int(GiV_Settings.numBatteries) @@ -64,7 +66,9 @@ def runAll(): client.update_battery_registers(BatRegCache, battery_number=x) GEBat=Battery.from_orm(BatRegCache) batteries[GEBat.battery_serial_number]=GEBat.dict() - + # endtime=datetime.datetime.now() + # logger.error("End time for library invertor call: "+ datetime.datetime.strftime(endtime,"%H:%M:%S")) + # logger.error("End time for library invertor call: "+ str(endtime-starttime)) logger.info("Invertor connection successful, registers retrieved") except: e = sys.exc_info() @@ -317,7 +321,7 @@ def runAll(): if GEInv.meter_type==1: metertype="EM115" if GEInv.meter_type==0: metertype="EM418" invertor['Meter Type']=metertype - invertor['Invertor Type']= GEInv.inverter_model.name + invertor['Invertor Type']= GEInv.inverter_model invertor['Invertor Temperature']=GEInv.temp_inverter_heatsink #Get Battery Details @@ -382,7 +386,7 @@ def runAll(): def publishOutput(array,SN): tempoutput={} tempoutput=iterate_dict(array) - + HAMQTT.publish_discovery(tempoutput,SN) if GiV_Settings.MQTT_Output.lower()=="true": from mqtt import GivMQTT logger.info("Publish all to MQTT") diff --git a/src/settings_template.py b/src/settings_template.py index 579dadc2..319d3cbc 100644 --- a/src/settings_template.py +++ b/src/settings_template.py @@ -3,9 +3,9 @@ class GiV_Settings: invertorIP="" #Required - IP address of Invertor on local network numBatteries="" #Debug Settings - Log_Level="True" #Optional - Enables verbose debug "True" or "False". + Log_Level="Error" #Optional - Enables logging level. Default is "Error", but can be "Info" or "Debug" Debug_File_Location="" #Optional - Location of logs (Default is console) - Print_Raw_Registers="False" #Optional - "True" prints all raw registers to the MQTT broker + Print_Raw_Registers="True" #Optional - "True" publishes all available registers. #MQTT Output Settings MQTT_Output="" #True or False MQTT_Address="" #IP address of MQTT broker (local or remote) diff --git a/src/startup.sh b/src/startup.sh index 75da6fd8..7aca0ef0 100644 --- a/src/startup.sh +++ b/src/startup.sh @@ -51,10 +51,12 @@ else printf " influxOrg=\"$INFLUX_ORG\"\n" >> settings.py fi +#TODO Update givTCP if a newer release is available + if [ "$MQTT_ADDRESS" = "127.0.0.1" ] #Only run Mosquitto if its using local broker then echo Starting Mosquitto on port "$MQTT_PORT" - /usr/sbin/mosquitto -p "$MQTT_PORT" & #Run local MQTT brker as default + /usr/sbin/mosquitto -p "$MQTT_PORT" & #Run local MQTT broker as default fi if [ "$SELF_RUN" = "True" ] #Only run Schedule if requested @@ -65,3 +67,5 @@ fi echo Starting Gunicorn on port 6345 gunicorn -w 3 -b :6345 REST:giv_api #Use for on-demand read and control + +