@ -40,6 +40,7 @@ class InfluxOutputPlugin(OutputPluginFactory):
def __init__ ( self , url , token , * * params ) :
"""
Initialize InfluxOutputPlugin
https : / / influxdb - client . readthedocs . io / en / stable / api . html #influxdbclient
The following targets must be present in your InfluxDB . This does not
automatically create anything for You .
@ -69,8 +70,12 @@ class InfluxOutputPlugin(OutputPluginFactory):
self . _org = params . get ( ' org ' , ' ' )
self . _measurement = params . get ( ' measurement ' , f ' inverter,host= { socket . gethostname ( ) } ' )
client = InfluxDBClient ( url , token , bucket = self . _bucket )
self . api = client . write_api ( )
with InfluxDBClient ( url , token , bucket = self . _bucket ) as self . client :
self . api = self . client . write_api ( )
def disco ( self , * * params ) :
self . client . close ( ) # Shutdown the client
return
def store_status ( self , response , * * params ) :
"""
@ -103,6 +108,9 @@ class InfluxOutputPlugin(OutputPluginFactory):
# InfluxDB requires nanoseconds
ctime = int ( utctime . timestamp ( ) * 1e9 )
if HOYMILES_DEBUG_LOGGING :
logging . info ( f ' InfluxDB: utctime: { utctime } ' )
# AC Data
phase_id = 0
for phase in data [ ' phases ' ] :
@ -136,6 +144,9 @@ class InfluxOutputPlugin(OutputPluginFactory):
data_stack . append ( f ' { measurement } ,type=YieldToday value= { data [ " yield_today " ] / 1000 : .3f } { ctime } ' )
data_stack . append ( f ' { measurement } ,type=Efficiency value= { data [ " efficiency " ] : .2f } { ctime } ' )
if HOYMILES_DEBUG_LOGGING :
#logging.debug(f'INFLUX data to DB: {data_stack}')
pass
self . api . write ( self . _bucket , self . _org , data_stack )
class MqttOutputPlugin ( OutputPluginFactory ) :
@ -197,6 +208,12 @@ class MqttOutputPlugin(OutputPluginFactory):
def disco ( self , * * params ) :
self . client . loop_stop ( ) # Stop loop
self . client . disconnect ( ) # disconnect
return
def info2mqtt ( self , mqtt_topic , mqtt_data ) :
for mqtt_key in mqtt_data :
self . client . publish ( f ' { mqtt_topic [ " topic " ] } / { mqtt_key } ' , mqtt_data [ mqtt_key ] , self . qos , self . ret )
return
def store_status ( self , response , * * params ) :
"""
@ -210,13 +227,18 @@ class MqttOutputPlugin(OutputPluginFactory):
"""
data = response . __dict__ ( )
topic = f ' { data . get ( " inverter_name " , " hoymiles " ) } / { data . get ( " inverter_ser " , None ) } '
topic = params . get ( ' topic ' , None )
if not topic :
topic = f ' { data . get ( " inverter_name " , " hoymiles " ) } / { data . get ( " inverter_ser " , None ) } '
if HOYMILES_DEBUG_LOGGING :
logging . info ( f ' MQTT-topic: { topic } data-type: { type ( response ) } ' )
if isinstance ( response , StatusResponse ) :
# Global Head
if data [ ' time ' ] is not None :
self . client . publish ( f ' { topic } /time ' , data [ ' time ' ] . strftime ( " %d . % m. % y - % H: % M: % S " ) , self . qos , self . ret )
self . client . publish ( f ' { topic } /time ' , data [ ' time ' ] . strftime ( " %d . % m. % YT % H: % M: % S " ) , self . qos , self . ret )
# AC Data
phase_id = 0
@ -234,12 +256,16 @@ class MqttOutputPlugin(OutputPluginFactory):
string_id = 0
string_sum_power = 0
for string in data [ ' strings ' ] :
self . client . publish ( f ' { topic } /emeter-dc/ { string_id } /voltage ' , string [ ' voltage ' ] , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_id } /current ' , string [ ' current ' ] , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_id } /power ' , string [ ' power ' ] , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_id } /YieldDay ' , string [ ' energy_daily ' ] , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_id } /YieldTotal ' , string [ ' energy_total ' ] / 1000 , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_id } /Irradiation ' , string [ ' irradiation ' ] , self . qos , self . ret )
if ' name ' in string :
string_name = string [ ' name ' ] . replace ( " " , " _ " )
else :
string_name = string_id
self . client . publish ( f ' { topic } /emeter-dc/ { string_name } /voltage ' , string [ ' voltage ' ] , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_name } /current ' , string [ ' current ' ] , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_name } /power ' , string [ ' power ' ] , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_name } /YieldDay ' , string [ ' energy_daily ' ] , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_name } /YieldTotal ' , string [ ' energy_total ' ] / 1000 , self . qos , self . ret )
self . client . publish ( f ' { topic } /emeter-dc/ { string_name } /Irradiation ' , string [ ' irradiation ' ] , self . qos , self . ret )
string_id = string_id + 1
string_sum_power + = string [ ' power ' ]
@ -297,6 +323,9 @@ class VzInverterOutput:
ts = int ( round ( data [ ' time ' ] . timestamp ( ) * 1000 ) )
if HOYMILES_DEBUG_LOGGING :
logging . info ( f ' Volkszaehler-Timestamp: { ts } ' )
# AC Data
phase_id = 0
for phase in data [ ' phases ' ] :
@ -329,6 +358,7 @@ class VzInverterOutput:
if data [ ' yield_today ' ] is not None :
self . try_publish ( ts , f ' yield_today ' , data [ ' yield_today ' ] )
self . try_publish ( ts , f ' efficiency ' , data [ ' efficiency ' ] )
return
def try_publish ( self , ts , ctype , value ) :
if not ctype in self . channels :
@ -340,9 +370,12 @@ class VzInverterOutput:
url = f ' { self . baseurl } /data/ { uid } .json?operation=add&ts= { ts } &value= { value } '
if uid == None :
if HOYMILES_DEBUG_LOGGING :
logging . warnin g( f ' ctype \" { ctype } \" has no configured uid-value in ahoy.yml ' )
logging . debu g( f ' ctype \" { ctype } \" has no configured uid-value in ahoy.yml ' )
return
if HOYMILES_DEBUG_LOGGING :
logging . debug ( f ' VZ-url: { url } ' )
try :
r = self . session . get ( url )
if r . status_code == 404 :
@ -353,6 +386,7 @@ class VzInverterOutput:
raise ValueError ( f ' Transmit result { url } ' )
except ConnectionError as e :
raise ValueError ( f ' Could not connect VZ-DB { type ( e ) } { e . keys ( ) } ' )
return
class VolkszaehlerOutputPlugin ( OutputPluginFactory ) :
def __init__ ( self , config , * * params ) :
@ -373,13 +407,17 @@ class VolkszaehlerOutputPlugin(OutputPluginFactory):
exit ( 1 )
self . session = requests . Session ( )
self . inverters = dict ( )
self . inverters = dict ( )
for inverterconfig in config . get ( ' inverters ' , [ ] ) :
serial = inverterconfig . get ( ' serial ' )
output = VzInverterOutput ( inverterconfig , self . session )
self . inverters [ serial ] = output
def disco ( self , * * params ) :
self . session . close ( ) # closing the connection
return
def store_status ( self , response , * * params ) :
"""
Publish StatusResponse object
@ -404,3 +442,4 @@ class VolkszaehlerOutputPlugin(OutputPluginFactory):
output . store_status ( data , self . session )
except ValueError as e :
logging . warning ( ' Could not send data to volkszaehler instance: %s ' % e )
return