diff --git a/CHANGELOG.md b/CHANGELOG.md index c764012..751b081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,3 +138,14 @@ Fix version ### Fixed 1. Reliable messages delivery to MQTT/Domoticz. + +## [1.7] - 2022-02-23 +HomeAssistant support + +### Added +1. Home Assistant support (via MQTT) - curtesy of @pablolite code + +### Changed +1. Minor cleanups. + +### Fixed diff --git a/InverterData.py b/InverterData.py index b7af8b5..379e3fc 100755 --- a/InverterData.py +++ b/InverterData.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # Script gathering solar data from Sofar Solar Inverter (K-TLX) via logger module LSW-3/LSE -# by Michalux (based on DEYE script by jlopez77) -# Version: 1.66 +# by Michalux (based on DEYE script by jlopez77, HA initial code by pablolite). +# Version: 1.7 # import sys @@ -11,7 +11,6 @@ import libscrc import json import paho.mqtt.client as paho -import paho.mqtt.publish as mqttpublish import os import configparser import datetime @@ -84,6 +83,7 @@ def PrepareDomoticzData(DData, idx, svalue): ifpass=configParser.get('InfluxDB', 'influxdb_password') ifdb=configParser.get('InfluxDB', 'influxdb_dbname') DomoticzSupport=configParser.get('Domoticz', 'domoticz_support') +HomeAssistantSupport=configParser.get('HomeAssistant', 'homeassistant_support') # END CONFIG timestamp=str(datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')) @@ -108,6 +108,7 @@ def PrepareDomoticzData(DData, idx, svalue): totaltime=0 PMData=[] DomoticzData=[] +HomeAssistantData=[] while chunks<2: if verbose=="1": print("*** Chunk no: ", chunks); @@ -210,6 +211,7 @@ def PrepareDomoticzData(DData, idx, svalue): if prometheus=="1" and graph==1: PMetrics(metric_name, metric_type, label_name, label_value, response); if influxdb=="1" and graph==1: PrepareInfluxData(InfluxData, metric_name.split('_')[0]+"_"+label_value, response); if DomoticzSupport=="1" and DomoticzIdx>0: PrepareDomoticzData(DomoticzData, DomoticzIdx, response); + if HomeAssistantSupport=="1": HomeAssistantData.append([title, ratio, unit, metric_type, metric_name, label_name, label_value, response, register]); if unit!="": output=output+"\""+ title + " (" + unit + ")" + "\":" + str(response)+"," else: @@ -222,6 +224,7 @@ def PrepareDomoticzData(DData, idx, svalue): if prometheus=="1" and graph==1: PMetrics(metric_name, metric_type, label_name, label_value, (totalpower*1000)); if influxdb=="1" and graph==1: PrepareInfluxData(InfluxData, metric_name.split('_')[0]+"_"+label_value, totalpower); if DomoticzSupport=="1" and DomoticzIdx>0: PrepareDomoticzData(DomoticzData, DomoticzIdx, response); + if HomeAssistantSupport=="1": HomeAssistantData.append([title, ratio, unit, metric_type, metric_name, label_name, label_value, response, (totalpower*1000)]); if hexpos=='0x0017': totaltime+=response*ratio*65536; if hexpos=='0x0018': totaltime+=response*ratio @@ -230,6 +233,7 @@ def PrepareDomoticzData(DData, idx, svalue): if prometheus=="1" and graph==1: PMetrics(metric_name, metric_type, label_name, label_value, totaltime); if influxdb=="1" and graph==1: PrepareInfluxData(InfluxData, metric_name.split('_')[0]+"_"+label_value, totaltime); if DomoticzSupport=="1" and DomoticzIdx>0: PrepareDomoticzData(DomoticzData, DomoticzIdx, response); + if HomeAssistantSupport=="1": HomeAssistantData.append([title, ratio, unit, metric_type, metric_name, label_name, label_value, response, totaltime]); a+=1 if chunks==0: pini=reg_start2 @@ -250,7 +254,7 @@ def PrepareDomoticzData(DData, idx, svalue): Write2InfluxDB(InfluxData) if verbose=="1": print("Influx data: ",InfluxData); -# MQTT and Domoticz integration +# MQTT integration (Domoticz, HA, pure MQTT) if mqtt==1 and invstatus==1: # Initialise MQTT connection client=paho.Client("inverter") @@ -273,9 +277,45 @@ def PrepareDomoticzData(DData, idx, svalue): result=client.publish(mqtt_topic+"/attributes",output) result.wait_for_publish() if result.is_published: - print("*** Data has been succesfully published to MQTT with topic: "+mqtt_topic+"/attributes") + if verbose==1: print("*** Data has been succesfully published to MQTT with topic: "+mqtt_topic+"/attributes") else: print("Error publishing data to MQTT") + if HomeAssistantSupport=="1": + if verbose=="1": print("*** MQTT messages for HomeAssistant:"); + # Send messages in case of unexpected disconnection + client.will_set("Sofar/Logger/"+str(inverter_sn)+"/state/connected","false") + # Send status of device: enabled = true + result=client.publish("Sofar/Logger/"+str(inverter_sn)+"/enabled","true") + result.wait_for_publish() + if not result.is_published: + print("Error publishing device status for HomeAssistant to MQTT") + # Send state of device: connected = true + result=client.publish("Sofar/Logger/"+str(inverter_sn)+"/state/connected","true") + result.wait_for_publish() + if not result.is_published: + print("Error publishing device state for HomeAssistant to MQTT") + HAcount=0 + for mqtt_data in HomeAssistantData: + # Sensors for ENERGY module with kWh, Wh, W + #if mqtt_data[2]=="kWh" or mqtt_data[2]=="Wh" or mqtt_data[2]=="W": + if mqtt_data[2] in ['kWh', 'Wh', 'W']: + # Send auto-discover device sensor template + result=client.publish("homeassistant/sensor/SofarLogger/"+str(inverter_sn)+"_"+str(HAcount)+"/config","{\"avty\":{\"topic\":\"Sofar/Logger/"+str(inverter_sn)+"/state/connected\",\"payload_available\":\"true\",\"payload_not_available\":\"false\"},\"~\":\"Sofar/Logger/"+str(inverter_sn)+"/\",\"device\":{\"ids\":\""+str(inverter_sn)+"\",\"mf\":\"Sofar\",\"name\":\"WLS-3\",\"sw\":\"x.x.x\"},\"name\":\""+(mqtt_data[0])+" ["+(mqtt_data[2])+"]\",\"uniq_id\":\""+str(inverter_sn)+"_"+str(HAcount)+"\",\"qos\":0,\"unit_of_meas\":\""+(mqtt_data[2])+"\",\"stat_t\":\"~state/"+(mqtt_data[4])+(mqtt_data[6])+"\",\"val_tpl\":\"{{ value | round(5) }}\",\"dev_cla\":\"energy\",\"state_class\":\"total_increasing\"}") + # Rest of the sensors + else: + # Send auto-discover device sensor template + result=client.publish("homeassistant/sensor/SofarLogger/"+str(inverter_sn)+"_"+str(HAcount)+"/config","{\"avty\":{\"topic\":\"Sofar/Logger/"+str(inverter_sn)+"/state/connected\",\"payload_available\":\"true\",\"payload_not_available\":\"false\"},\"~\":\"Sofar/Logger/"+str(inverter_sn)+"/\",\"device\":{\"ids\":\""+str(inverter_sn)+"\",\"mf\":\"Sofar\",\"name\":\"WLS-3\",\"sw\":\"x.x.x\"},\"name\":\""+(mqtt_data[0])+" ["+(mqtt_data[2])+"]\",\"uniq_id\":\""+str(inverter_sn)+"_"+str(HAcount)+"\",\"qos\":0,\"unit_of_meas\":\""+(mqtt_data[2])+"\",\"stat_t\":\"~state/"+(mqtt_data[4])+(mqtt_data[6])+"\",\"val_tpl\":\"{{ value | round(5) }}\",\"dev_cla\":\"current\",\"state_class\":\"measurement\"}") + result.wait_for_publish() + if not result.is_published: + print("[",str(HAcount),"]", "Error publishing data for HomeAssistant to MQTT") + else: + if verbose=="1": print("[",str(HAcount),"]", mqtt_data[0], ": ", mqtt_data[7]); + # Send sensor values data + result=client.publish("Sofar/Logger/"+str(inverter_sn)+"/state/"+(mqtt_data[4])+(mqtt_data[6]), (mqtt_data[7])) + result.wait_for_publish() + if not result.is_published: + print("[",str(HAcount),"]","Error publishing data for HomeAssistant to MQTT") + HAcount+=1 client.loop_stop() client.disconnect() print("*** JSON output:") diff --git a/README.md b/README.md index 10772e0..d533f0f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ REMARK: To make it work with other inverter brand/model connected via LSW-3/LSE the register's addresses in the .xml files accordingly and change register start/end numbers in config.cfg *Thanks to @jlopez77 https://github.com/jlopez77 for logger/MODBUS protocol code.* +*Thanks to @pablolite for HomeAssistant initial code.* # Required python modules To run, script requires following python modules: @@ -61,6 +62,9 @@ mqtt_cacert= # CA certificate path/filename [Domoticz] domoticz_support=0 # 0: disabled, 1: enabled +[HomeAssistant] +homeassistant_support=0 # 0: disabled, 1: enabled + Files SOFARMap.xml and SOFARHWMap.xml contain MODBUS inverter's registers mapping for Sofar Solar K-TLX product line and Prometheus/InfluxDB metrics configuration. Edit i.e. to get captions in a different language, change Prometheus/InfluxDB metrics names or @@ -161,10 +165,10 @@ You tell me :) Feel free to suggest :) If You want to rewrite or/add change anything - please fork Your own project. -# MQTT Support (Domoticz compatible) +# Domoticz/MQTT Support ``` 1. JSON_attributes_topic (unless Domoticz support enabled !): "mqtt_topic/attributes" - 2. For TLS support You'll need at least CA Certificate and TLS enabled MQTT + 2. For MQTT TLS support You'll need at least CA Certificate and TLS enabled MQTT To enable TLS for Mosquitto look i.e here: http://www.steves-internet-guide.com/mosquitto-tls/ 3. To turn Domoticz support on: a) enable it in config.cfg @@ -175,6 +179,16 @@ If You want to rewrite or/add change anything - please fork Your own project. WARNING: When enabled, Domoticz support disables normal MQTT message delivery (all values in one message). 3. Tested with Mosquitto MQTT server (both with and without TLS) and Domoticz 2021.1 ``` +# HomeAssistant (via MQTT) Support (prepared/tested by @pablolite, optimized by Michalux) +``` + 1. For MQTT/TLS support You'll need at least CA Certificate and TLS enabled MQTT + To enable TLS for Mosquitto look i.e here: http://www.steves-internet-guide.com/mosquitto-tls/ + 2. To turn HomeAssistant support on, enable it in config.cfg + 3. Hardcoded MQTT topic names: + a) Auto-discovery: homeassistant/sensor/SofarLogger/{Inverter's SN}_ID/config + b) Values: Sofar/Logger/{Inverter's SN}/state/{Param name} + c) Inverter's status: Sofar/Logger/{Inverter's SN}/enabled +``` # Prometheus+Grafana support ``` Steps to run Prometheus+Grafana support: diff --git a/config.cfg b/config.cfg index 21840e5..b434f3c 100644 --- a/config.cfg +++ b/config.cfg @@ -37,3 +37,6 @@ mqtt_cacert= [Domoticz] domoticz_support=0 + +[HomeAssistant] +homeassistant_support=0