Skip to content

Commit

Permalink
1.2.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Hans IJntema committed Mar 5, 2022
1 parent 128818d commit bc89f77
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 105 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ __pycache__
venv
.idea
notes.txt
ansible.sh
config.py


inverter_msg.py
133 changes: 76 additions & 57 deletions config.rename.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,57 +1,76 @@
#!/usr/bin/python3

"""
Rename to config.py
Configure:
- MQTT client
- Debug level
"""

# [ LOGLEVELS ]
# DEBUG, INFO, WARNING, ERROR, CRITICAL
loglevel = "INFO"

# Using local dns names was not always reliable with PAHO
MQTT_BROKER = "192.168.1.1"
MQTT_PORT = 1883
MQTT_CLIENT_UNIQ = 'mqtt-trannergy'
MQTT_QOS = 1
MQTT_USERNAME = "ijntema"
MQTT_PASSWORD = "mosquitto0000"

# Max nrof MQTT messages per second transmitted by MQTT client
# Set to 0 for unlimited rate
MQTT_RATE = 100

MQTT_TOPIC_PREFIX = "solar/trannergy/roof_w"

# [ InfluxDB ]
# Add a influxdb database tag, for Telegraf processing (database:INFLUXDB)
# This is not required for core functionality of this parser
# Set to None if Telegraf is not used
INFLUXDB = "solar"
#INFLUXDB = None

#[ TRANNERGY INVERTER ]
INV_SERIAL = "PVL5400N177E4008"

# Required when using TCPCLIENT reader
INV_SERIAL_LOGGER = 625830567
INV_IP = "192.168.1.53"
INV_TCPCLIENTPORT = 8899

# NROF parameter reads from inverter per hour (60 equals every minute)
INV_READ_RATE = 60

# Select one of the two available readers
#
# If supported: can read high frequent all inverter parameters
INV_READER="TCPCLIENT"

# Inverter sends every 5 minutes an update of all parameters
# Configure in inverter
# Advanced settings
# Server: ip address to ip where this python script is running; port 3203
#INV_READER="LISTEN"
#!/usr/bin/python3

"""
Rename to config.py
Configure:
- MQTT client
- Debug level
"""

# [ LOGLEVELS ]
# DEBUG, INFO, WARNING, ERROR, CRITICAL
loglevel = "INFO"

# Using local dns names was not always reliable with PAHO
MQTT_BROKER = "192.168.1.1"
MQTT_PORT = 1883
# has to be unique across all MQTT clients
MQTT_CLIENT_UNIQ = 'mqtt-trannergy'
MQTT_QOS = 1
MQTT_USERNAME = "username"
MQTT_PASSWORD = "secret"

# Max nrof MQTT messages per second transmitted by MQTT client
# Set to 0 for unlimited rate
MQTT_RATE = 100

MQTT_TOPIC_PREFIX = "solar/trannergy/roof_w"

# [ InfluxDB ]
# Add a influxdb database tag, for Telegraf processing.
# This is not required for core functionality of this parser
# Set to None if Telegraf is not used
INFLUXDB = "solar"
#INFLUXDB = None

#[ TRANNERGY INVERTER ]
# This is used to validate packages
INV_SERIAL = "PVL5400Nxxxxxxxx"

# Required when using TCPCLIENT reader
INV_SERIAL_LOGGER = 123456780
# ip address of the i nverter
INV_IP = "192.168.1.53"
INV_TCPCLIENTPORT = 8899

# NROF parameter reads from inverter per hour (60 equals every minute); only when INV_READER="TCPCLIENT"
INV_READ_RATE = 60

# Select one of the two available readers
# If supported: can read high(er) frequent all inverter parameters --> INV_READ_RATE
INV_READER="TCPCLIENT"

# Inverter sends every 5 minutes an update of all parameters
# When starting this script....be patient at least for 5minutes
# Configure in inverter:
# Advanced settings
# Remote Server
# Add Server B (ip address to ip where this python script is running; port 3203)
#INV_READER="LISTEN"

# [ Home Assistant ]
HA_DISCOVERY = True

# HA MQTT auto discovery only supports one level of hierarchy (eg "trannergy", and not "trannergy/roof_w")
HA_MQTT_DISCOVERY_TOPIC_PREFIX = "trannergy"

# Default is False; when True removes the auto config message when this program exits
HA_DELETECONFIG = False

# Discovery messages per hour
# At start-up, always a discovery message is send
# Default is 12 ==> 1 message every 5 minutes. If the MQTT broker is restarted
# it can take up to 5 minutes before the dsmr device re-appears in HA
HA_INTERVAL = 12
39 changes: 38 additions & 1 deletion mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"""

__version__ = "1.0.1"
__version__ = "1.0.2"
__author__ = "Hans IJntema"
__license__ = "GPLv3"

Expand Down Expand Up @@ -154,6 +154,10 @@ def __init__(self, mqtt_broker, mqtt_port, mqtt_client_id, mqtt_rate, mqtt_qos,
self.__queue = queue.Queue(maxsize = self.__maxqueuesize)
self.__mqtt.username_pw_set(username, password)

self.__status_topic = None
self.__status_payload = None
self.__status_retain = None


def __del__(self):
logger.info( f">>" )
Expand Down Expand Up @@ -197,6 +201,7 @@ def __on_connect(self, client, userdata, flags, rc):
if rc == 0:
logger.debug( f"Connected: client={client}; userdata={userdata}; flags={flags}; rc={rc}" )
self.__connected_flag=True
self.__set_status()
else:
logger.error( f"userdata={userdata}; flags={flags}; rc={rc}; {connack_dict[rc]}" )
self.__connected_flag=False
Expand Down Expand Up @@ -255,6 +260,38 @@ def __on_log(self, client, obj, level, buf):
logger.debug(f"obj={obj}; level={level}; buf={buf}")


def __set_status(self):
"""
Set status
:param str topic:
:param str payload:
:param int qos:
:param bool retain:
:return: None
"""

if self.__status_topic is not None:
self.do_publish(self.__status_topic, self.__status_payload, self.__status_retain)

def set_status(self, topic, payload=None, retain=False):
"""
Set status
Will store status & resend on a reconnect
:param str topic:
:param str payload:
:param bool retain:
:return: None
"""

self.__status_topic = topic
self.__status_payload = payload
self.__status_retain = retain
self.__set_status()



def will_set(self, topic, payload=None, qos=0, retain=False):
"""
Set last will/testament
Expand Down
42 changes: 32 additions & 10 deletions trannergy-mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
V1.0.0:
- Initial version
V1.1.0
- Extend with high(er) frequent read out via tcpclient
inspired by: https://github.com/jbouwh/omnikdatalogger/blob/dev/apps/omnikdatalogger/omnik/plugin_client/tcpclient.py
V1.2.0
- Enabled homeassistant autodiscovery
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand All @@ -30,7 +36,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

__version__ = "1.0.0"
__version__ = "1.2.2"
__author__ = "Hans IJntema"
__license__ = "GPLv3"

Expand All @@ -44,9 +50,11 @@
import config as cfg
import trannergy_serial as trannergy
import trannergy_parser as convert
#import hadiscovery as ha
import hadiscovery as ha
import mqtt as mqtt

import trannergy_tcpclient as tcpclient

from log import logger
logger.setLevel(cfg.loglevel)

Expand Down Expand Up @@ -102,16 +110,30 @@ def close(exit_code):
mqtt_stopper,
threads_stopper)

# SerialPort thread
telegram = list()
t_serial = trannergy.TaskReadSerial(trigger, threads_stopper, telegram)

# Select which reader will be used; setup SerialPort thread
try:
if cfg.INV_READER == "TCPCLIENT":
t_serial = tcpclient.TaskReadSerial(trigger, threads_stopper, telegram)
elif cfg.INV_READER == "LISTEN":
t_serial = trannergy.TaskReadSerial(trigger, threads_stopper, telegram)
else:
logger.error(f"Wrong READER specified {cfg.INV_READER}")
threads_stopper.set()
t_serial = None
except Exception as e:
logger.debug(f"Exception {e}")
threads_stopper.set()
t_serial = None
close(1)


# Telegram parser thread
t_parse = convert.ParseTelegrams(trigger, threads_stopper, t_mqtt, telegram)

# Send Home Assistant auto discovery MQTT's
#t_discovery = ha.Discovery(threads_stopper, t_mqtt, __version__)

t_discovery = ha.Discovery(threads_stopper, t_mqtt, __version__)

def exit_gracefully(signal, stackframe):
"""
Expand All @@ -137,19 +159,20 @@ def main():
# Start all threads
t_mqtt.start()
t_parse.start()
#t_discovery.start()
t_discovery.start()
t_serial.start()

# Set status to online
t_mqtt.do_publish(cfg.MQTT_TOPIC_PREFIX + "/status", "online", retain=True)
t_mqtt.set_status(cfg.MQTT_TOPIC_PREFIX + "/status", "online", retain=True)
t_mqtt.do_publish(cfg.MQTT_TOPIC_PREFIX + "/sw-version", f"{__version__}", retain=True)

# block till t_serial stops receiving telegrams/exits
t_serial.join()
logger.debug("t_serial.join exited; set stopper for other threats")
threads_stopper.set()

# Set status to offline
t_mqtt.do_publish(cfg.MQTT_TOPIC_PREFIX + "/status", "offline", retain=True)
t_mqtt.set_status(cfg.MQTT_TOPIC_PREFIX + "/status", "offline", retain=True)

# Todo check if MQTT queue is empty before setting stopper
# Use a simple delay of 1sec before closing mqtt
Expand All @@ -159,7 +182,6 @@ def main():
logger.debug("<<" )
return


# ------------------------------------------------------------------------------------
# Entry point
# ------------------------------------------------------------------------------------
Expand Down
28 changes: 11 additions & 17 deletions trannergy_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,30 +52,27 @@ def __init__(self, trigger, stopper, mqtt, telegram):
self.__mqtt = mqtt
self.__prevjsondict = {}


def __del__(self):
logger.debug(">>")


def __publish_telegram(self, json_dict):
# publish the dictionaries per topic
"""
publish the dictionary content
:param json_dict:
:return:
"""

# make resilient against double forward slashes in topic
topic = cfg.MQTT_TOPIC_PREFIX
topic = topic.replace('//', '/')
message = json.dumps(json_dict, sort_keys=True, separators=(',',':'))
message = json.dumps(json_dict, sort_keys=True, separators=(',', ':'))
self.__mqtt.do_publish(topic, message, retain=False)


def __decode_telegram_element(self, index, element, ts, listofjsondicts):
# logger.debug(f">> index={index}; element={element}")
pass


def __decode_telegrams(self, telegram):
"""
Args:
:param list telegram:
:param list telegram: counter and actual data
Returns:
Expand All @@ -93,7 +90,7 @@ def __decode_telegrams(self, telegram):

# Convert from binary and remove first character
# offset...deepcopy causes this or adding to a list; not sure why.
offset=2
offset = 2

# Build a dict of key:value, for MQTT JSON
values["timestamp"] = ts
Expand All @@ -102,13 +99,12 @@ def __decode_telegrams(self, telegram):
if cfg.INFLUXDB:
values["database"] = cfg.INFLUXDB


# Reference
# https://github.com/XtheOne/Inverter-Data-Logger/blob/master/InverterMsg.py
# Current Trannergy is one phase system
# For 3 phase, uncomment
values["msg"] = hexdata[24+offset:28+offset] #new
values["serial"] = binascii.unhexlify(hexdata[30 + offset:60 + offset]).decode('utf-8')
values["msg"] = hexdata[24+offset:28+offset]
values["serial"] = binascii.unhexlify(hexdata[30 + offset:62 + offset]).decode('utf-8')
values["temperature"] = float(int(hexdata[62+offset:66+offset], 16)) / 10.0
values["v_pv1"] = float(int(hexdata[66+offset:70+offset], 16)) / 10.0
values["v_pv2"] = float(int(hexdata[70+offset:74+offset], 16)) / 10.0
Expand Down Expand Up @@ -164,11 +160,9 @@ def __decode_telegrams(self, telegram):
# 22: 101, # main_fwver(101)
# 23: 121, } # slave_fwver(121)


logger.debug(f"Received values = {values}")
self.__publish_telegram(values)


def run(self):
logger.debug(">>")

Expand Down
Loading

0 comments on commit bc89f77

Please sign in to comment.