33
33
34
34
from inverter import Inverter
35
35
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
37
37
38
38
__logo = """
39
39
____ _ _ ____ __ __ ___ _____ _____
@@ -102,6 +102,9 @@ class InverterModBusToMQTT:
102
102
103
103
__max_precision : int = - 1
104
104
105
+ __write : bool = False
106
+ ''' enable / disable write mode - setting'''
107
+
105
108
__analyze_protocol : bool = False
106
109
''' enable / disable analyze mode'''
107
110
@@ -199,6 +202,7 @@ def init_invertermodbustomqtt(self):
199
202
protocol_version = str (self .__settings .get (section , 'protocol_version' ))
200
203
201
204
self .__analyze_protocol = self .__settings .getboolean (section , 'analyze_protocol' , fallback = False )
205
+ self .__write = self .__settings .getboolean (section , 'write' , fallback = False )
202
206
self .__analyze_protocol_save_load = self .__settings .getboolean (section , 'analyze_protocol_save_load' , fallback = False )
203
207
204
208
@@ -317,9 +321,17 @@ def on_connect(self, client, userdata, flags, rc):
317
321
self .__mqtt_connected = True
318
322
319
323
324
+ __write_topics : dict [str , registry_map_entry ] = {}
325
+
320
326
def on_message (self , client , userdata , msg ):
321
327
""" 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
+
323
335
324
336
def run (self ):
325
337
"""
@@ -340,6 +352,9 @@ def run(self):
340
352
341
353
print ("using serial number: " + self .__device_serial_number )
342
354
355
+ if self .__write :
356
+ self .enable_write ()
357
+
343
358
if self .__mqtt_discovery_enabled :
344
359
self .mqtt_discovery ()
345
360
@@ -424,6 +439,55 @@ def run(self):
424
439
# If all the inverters are not online because no power is being generated then we sleep for 1 min
425
440
time .sleep (self .__offline_interval )
426
441
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
+
427
491
def analyze_protocol (self , settings_dir : str = 'protocols' ):
428
492
print ("=== PROTOCOL ANALYZER ===" )
429
493
protocol_names : list [str ] = []
@@ -602,6 +666,8 @@ def mqtt_discovery(self):
602
666
if item .concatenate and item .register != item .concatenate_registers [0 ]:
603
667
continue #skip all except the first register so no duplicates
604
668
669
+ if item .write_mode == WriteMode .READDISABLED : #disabled
670
+ continue
605
671
606
672
clean_name = item .variable_name .lower ().replace (' ' , '_' )
607
673
@@ -620,13 +686,18 @@ def mqtt_discovery(self):
620
686
disc_payload ['device' ] = device
621
687
disc_payload ['name' ] = clean_name
622
688
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
624
695
625
696
if item .unit :
626
697
disc_payload ['unit_of_measurement' ] = item .unit
627
698
628
699
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"
630
701
631
702
self .__mqtt_client .publish (discovery_topic ,
632
703
json .dumps (disc_payload ),qos = 1 , retain = True )
0 commit comments