3333
3434from  inverter  import  Inverter 
3535
36- from  protocol_settings  import  protocol_settings ,Data_Type ,registry_map_entry ,Registry_Type 
36+ from  protocol_settings  import  protocol_settings ,Data_Type ,registry_map_entry ,Registry_Type , WriteMode 
3737
3838__logo  =  """ 
3939   ____                        _   _   ____  __  __  ___ _____ _____  
@@ -102,6 +102,9 @@ class InverterModBusToMQTT:
102102
103103    __max_precision  : int  =  - 1 
104104
105+     __write  : bool  =  False 
106+     ''' enable / disable write mode - setting''' 
107+ 
105108    __analyze_protocol  : bool  =  False 
106109    ''' enable / disable analyze mode''' 
107110
@@ -199,6 +202,7 @@ def init_invertermodbustomqtt(self):
199202            protocol_version  =  str (self .__settings .get (section , 'protocol_version' ))
200203
201204            self .__analyze_protocol  =  self .__settings .getboolean (section , 'analyze_protocol' , fallback = False )
205+             self .__write  =  self .__settings .getboolean (section , 'write' , fallback = False )
202206            self .__analyze_protocol_save_load  =  self .__settings .getboolean (section , 'analyze_protocol_save_load' , fallback = False )
203207
204208
@@ -317,9 +321,17 @@ def on_connect(self, client, userdata, flags, rc):
317321        self .__mqtt_connected  =  True 
318322
319323
324+     __write_topics  : dict [str , registry_map_entry ] =  {}
325+ 
320326    def  on_message (self , client , userdata , msg ):
321327        """ The callback for when a PUBLISH message is received from the server. """ 
322-         self .__log .info (msg .topic + " " + str (msg .payload ))
328+         self .__log .info (msg .topic + " " + str (msg .payload .decode ('utf-8' )))
329+ 
330+         #self.inverter.protocolSettings.validate_registry_entry 
331+         if  msg .topic  in  self .__write_topics :
332+             entry  =  self .__write_topics [msg .topic ]
333+             self .inverter .write_variable (entry , value = str (msg .payload .decode ('utf-8' )))
334+ 
323335
324336    def  run (self ):
325337        """ 
@@ -340,6 +352,9 @@ def run(self):
340352
341353        print ("using serial number: "  +  self .__device_serial_number )
342354
355+         if  self .__write :
356+             self .enable_write ()
357+ 
343358        if  self .__mqtt_discovery_enabled :
344359            self .mqtt_discovery ()
345360
@@ -424,6 +439,55 @@ def run(self):
424439                # If all the inverters are not online because no power is being generated then we sleep for 1 min 
425440                time .sleep (self .__offline_interval )
426441
442+ 
443+     def  enable_write (self ):
444+         """ 
445+         enable write to modbus; must pass tests. 
446+         """ 
447+         print ("Validating Protocol for Writing" )
448+         self .__write  =  False 
449+         score_percent  =  self .validate_protocol (Registry_Type .HOLDING )
450+         if (score_percent  >  90 ):
451+             self .__write  =  True 
452+             print ("enable write - validation passed" )
453+             
454+             self .__write_topics  =  {}
455+             #subscribe to write topics 
456+             for  entry  in  self .inverter .protocolSettings .holding_registry_map :
457+                 if  entry .write_mode  ==  WriteMode .WRITE :
458+                     #__write_topics 
459+                     topic  : str  =  self .__mqtt_topic  +  "/write/"  +  entry .variable_name .lower ().replace (' ' , '_' )
460+                     self .__write_topics [topic ] =  entry 
461+                     self .__mqtt_client .subscribe (topic )
462+     
463+     def  validate_protocol (self , registry_type  : Registry_Type  =  Registry_Type .INPUT ) ->  float :
464+         """ 
465+         validate protocol 
466+         """ 
467+ 
468+         score  : float  =  0 
469+         info  =  {}
470+         registry_map  : list [registry_map_entry ] =  self .inverter .protocolSettings .get_registry_map (registry_type )
471+         info  =  self .inverter .read_registry (registry_type )
472+ 
473+         for  value  in  registry_map :
474+             if  value .variable_name  in  info :
475+                 evaluate  =  True 
476+ 
477+                 if  value .concatenate  and  value .register  !=  value .concatenate_registers [0 ]: #only eval concated values once 
478+                     evaluate  =  False 
479+                   
480+                 if  evaluate :
481+                     score  =  score  +  self .inverter .protocolSettings .validate_registry_entry (value , info [value .variable_name ])
482+ 
483+         maxScore  =  len (registry_map )
484+         percent  =  score * 100 / maxScore 
485+         print ("validation score: "  +  str (score ) +  " of "  +  str (maxScore ) +  " : "  +  str (round (percent )) +  "%" )
486+         return  percent 
487+ 
488+ 
489+     
490+ 
427491    def  analyze_protocol (self , settings_dir  : str  =  'protocols' ):
428492        print ("=== PROTOCOL ANALYZER ===" )
429493        protocol_names  : list [str ] =  []
@@ -602,6 +666,8 @@ def mqtt_discovery(self):
602666            if  item .concatenate  and  item .register  !=  item .concatenate_registers [0 ]:
603667                continue  #skip all except the first register so no duplicates 
604668
669+             if  item .write_mode  ==  WriteMode .READDISABLED : #disabled 
670+                 continue 
605671
606672            clean_name  =  item .variable_name .lower ().replace (' ' , '_' )
607673
@@ -620,13 +686,18 @@ def mqtt_discovery(self):
620686            disc_payload ['device' ] =  device 
621687            disc_payload ['name' ] =  clean_name 
622688            disc_payload ['unique_id' ] =  "hotnoob_"  +  self .__device_serial_number  +  "_" + clean_name 
623-             disc_payload ['state_topic' ] =  self .__mqtt_topic  +  "/" + clean_name 
689+ 
690+             writePrefix  =  "" 
691+             if  self .__write  and  item .write_mode  ==  WriteMode .WRITE :
692+                 writePrefix  =  ""  #home assistant doesnt like write prefix 
693+ 
694+             disc_payload ['state_topic' ] =  self .__mqtt_topic  + writePrefix +  "/" + clean_name 
624695
625696            if  item .unit :
626697                disc_payload ['unit_of_measurement' ] =  item .unit 
627698
628699
629-             discovery_topic  =  self .__mqtt_discovery_topic + "/sensor/inverter-"  +  self .__device_serial_number   +  "/"  +  disc_payload ['name' ].replace (' ' , '_' ) +  "/config" 
700+             discovery_topic  =  self .__mqtt_discovery_topic + "/sensor/inverter-"  +  self .__device_serial_number   +  writePrefix   +   "/"  +  disc_payload ['name' ].replace (' ' , '_' ) +  "/config" 
630701
631702            self .__mqtt_client .publish (discovery_topic ,
632703                                       json .dumps (disc_payload ),qos = 1 , retain = True )
0 commit comments