@ -13,28 +13,6 @@ import paho.mqtt.client
import yaml
import yaml
from yaml . loader import SafeLoader
from yaml . loader import SafeLoader
parser = argparse . ArgumentParser ( description = ' Ahoy - Hoymiles solar inverter gateway ' )
parser . add_argument ( " -c " , " --config-file " , nargs = " ? " ,
help = " configuration file " )
global_config = parser . parse_args ( )
if global_config . config_file :
with open ( global_config . config_file ) as yf :
cfg = yaml . load ( yf , Loader = SafeLoader )
else :
with open ( global_config . config_file ) as yf :
cfg = yaml . load ( ' ahoy.yml ' , Loader = SafeLoader )
radio = RF24 ( 22 , 0 , 1000000 )
hmradio = hoymiles . HoymilesNRF ( device = radio )
mqtt_client = None
command_queue = { }
mqtt_command_topic_subs = [ ]
hoymiles . HOYMILES_TRANSACTION_LOGGING = True
hoymiles . HOYMILES_DEBUG_LOGGING = True
def main_loop ( ) :
def main_loop ( ) :
inverters = [
inverters = [
inverter for inverter in ahoy_config . get ( ' inverters ' , [ ] )
inverter for inverter in ahoy_config . get ( ' inverters ' , [ ] )
@ -45,16 +23,19 @@ def main_loop():
print ( f ' Poll inverter { inverter [ " serial " ] } ' )
print ( f ' Poll inverter { inverter [ " serial " ] } ' )
poll_inverter ( inverter )
poll_inverter ( inverter )
def poll_inverter ( inverter ) :
def poll_inverter ( inverter , retries = 4 ) :
inverter_ser = inverter . get ( ' serial ' )
inverter_ser = inverter . get ( ' serial ' )
dtu_ser = ahoy_config . get ( ' dtu ' , { } ) . get ( ' serial ' )
dtu_ser = ahoy_config . get ( ' dtu ' , { } ) . get ( ' serial ' )
if len ( command_queue [ str ( inverter_ser ) ] ) > 0 :
# Queue at least status data request
command_queue [ str ( inverter_ser ) ] . append ( hoymiles . compose_set_time_payload ( ) )
# Putt all queued commands for current inverter on air
while len ( command_queue [ str ( inverter_ser ) ] ) > 0 :
payload = command_queue [ str ( inverter_ser ) ] . pop ( 0 )
payload = command_queue [ str ( inverter_ser ) ] . pop ( 0 )
else :
payload = hoymiles . compose_set_time_payload ( )
payload_ttl = 4
# Send payload {ttl}-times until we get at least one reponse
payload_ttl = retries
while payload_ttl > 0 :
while payload_ttl > 0 :
payload_ttl = payload_ttl - 1
payload_ttl = payload_ttl - 1
com = hoymiles . InverterTransaction (
com = hoymiles . InverterTransaction (
@ -76,6 +57,7 @@ def poll_inverter(inverter):
print ( f ' Error while retrieving data: { e } ' )
print ( f ' Error while retrieving data: { e } ' )
pass
pass
# Handle the response data if any
if response :
if response :
dt = datetime . now ( )
dt = datetime . now ( )
print ( f ' { dt } Payload: ' + hoymiles . hexify_payload ( response ) )
print ( f ' { dt } Payload: ' + hoymiles . hexify_payload ( response ) )
@ -170,8 +152,51 @@ def mqtt_on_command(client, userdata, message):
hoymiles . frame_payload ( payload [ 1 : ] ) )
hoymiles . frame_payload ( payload [ 1 : ] ) )
if __name__ == ' __main__ ' :
if __name__ == ' __main__ ' :
parser = argparse . ArgumentParser ( description = ' Ahoy - Hoymiles solar inverter gateway ' , prog = " hoymiles " )
parser . add_argument ( " -c " , " --config-file " , nargs = " ? " , required = True ,
help = " configuration file " )
parser . add_argument ( " --log-transactions " , action = " store_true " , default = False ,
help = " Enable transaction logging output " )
parser . add_argument ( " --verbose " , action = " store_true " , default = False ,
help = " Enable debug output " )
global_config = parser . parse_args ( )
# Load ahoy.yml config file
try :
if isinstance ( global_config . config_file , str ) == True :
with open ( global_config . config_file , ' r ' ) as yf :
cfg = yaml . load ( yf , Loader = SafeLoader )
else :
with open ( ' ahoy.yml ' , ' r ' ) as yf :
cfg = yaml . load ( yf , Loader = SafeLoader )
except FileNotFoundError :
print ( " Could not load config file. Try --help " )
sys . exit ( 2 )
except yaml . YAMLError as ye :
print ( ' Failed to load config frile {global_config.config_file} : {ye} ' )
sys . exit ( 1 )
ahoy_config = dict ( cfg . get ( ' ahoy ' , { } ) )
ahoy_config = dict ( cfg . get ( ' ahoy ' , { } ) )
# Prepare for multiple transceivers, makes them configurable (currently
# only one supported)
for radio_config in ahoy_config . get ( ' nrf ' , [ { } ] ) :
radio = RF24 (
radio_config . get ( ' ce_pin ' , 22 ) ,
radio_config . get ( ' cs_pin ' , 0 ) ,
radio_config . get ( ' spispeed ' , 1000000 ) )
hmradio = hoymiles . HoymilesNRF ( device = radio )
mqtt_client = None
command_queue = { }
mqtt_command_topic_subs = [ ]
if global_config . log_transactions :
hoymiles . HOYMILES_TRANSACTION_LOGGING = True
if global_config . verbose :
hoymiles . HOYMILES_DEBUG_LOGGING = True
mqtt_config = ahoy_config . get ( ' mqtt ' , [ ] )
mqtt_config = ahoy_config . get ( ' mqtt ' , [ ] )
if not mqtt_config . get ( ' disabled ' , False ) :
if not mqtt_config . get ( ' disabled ' , False ) :
mqtt_client = paho . mqtt . client . Client ( )
mqtt_client = paho . mqtt . client . Client ( )
@ -202,9 +227,13 @@ if __name__ == '__main__':
loop_interval = ahoy_config . get ( ' interval ' , 1 )
loop_interval = ahoy_config . get ( ' interval ' , 1 )
try :
try :
while True :
while True :
t_loop_start = time . time ( )
main_loop ( )
main_loop ( )
if loop_interval :
print ( ' ' , end = ' ' , flush = True )
if loop_interval > 0 and ( time . time ( ) - t_loop_start ) < loop_interval :
time . sleep ( time . time ( ) % loop_interval )
time . sleep ( time . time ( ) % loop_interval )
except KeyboardInterrupt :
except KeyboardInterrupt :