Browse Source

Merge pull request #629 from PaeserBastelstube/main

RPI:MQTT handle QoS, Retain and Last-Will
pull/684/head
Lukas Pusch 2 years ago
committed by GitHub
parent
commit
208f9e5ab7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      tools/rpi/ahoy.yml.example
  2. 23
      tools/rpi/hoymiles/__main__.py
  3. 57
      tools/rpi/hoymiles/outputs.py

5
tools/rpi/ahoy.yml.example

@ -28,6 +28,11 @@ ahoy:
password: 'password' password: 'password'
useTLS: False useTLS: False
insecureTLS: False #set True for e.g. self signed certificates. insecureTLS: False #set True for e.g. self signed certificates.
QoS: 0
Retain: True
last_will:
topic: Appelweg_PV/114181807700 # defaults to 'hoymiles/{serial}'
payload: "LAST-WILL-MESSAGE: Please check my HOST and Process!"
# Influx2 output # Influx2 output
influxdb: influxdb:

23
tools/rpi/hoymiles/__main__.py

@ -17,10 +17,31 @@ from suntimes import SunTimes
import argparse import argparse
import yaml import yaml
from yaml.loader import SafeLoader from yaml.loader import SafeLoader
# import paho.mqtt.client
import hoymiles import hoymiles
import logging import logging
################################################################################
""" Signal Handler """
################################################################################
# from signal import signal, Signals, SIGINT, SIGTERM, SIGKILL, SIGHUP
from signal import *
def signal_handler(sig_num, frame):
signame = Signals(sig_num).name
logging.info(f'Stop by Signal {signame} ({sig_num})')
print (f'Stop by Signal <{signame}> ({sig_num}) at: {time.strftime("%d.%m.%Y %H:%M:%S")}')
if mqtt_client:
mqtt_client.disco()
sys.exit(0)
signal(SIGINT, signal_handler) # Interrupt from keyboard (CTRL + C)
signal(SIGTERM, signal_handler) # Signal Handler from terminating processes
signal(SIGHUP, signal_handler) # Hangup detected on controlling terminal or death of controlling process
# signal(SIGKILL, signal_handler) # Signal Handler SIGKILL and SIGSTOP cannot be caught, blocked, or ignored!!
################################################################################
################################################################################
class InfoCommands(IntEnum): class InfoCommands(IntEnum):
InverterDevInform_Simple = 0 # 0x00 InverterDevInform_Simple = 0 # 0x00
InverterDevInform_All = 1 # 0x01 InverterDevInform_All = 1 # 0x01

57
tools/rpi/hoymiles/outputs.py

@ -179,10 +179,23 @@ class MqttOutputPlugin(OutputPluginFactory):
mqtt_client.tls_set() mqtt_client.tls_set()
mqtt_client.tls_insecure_set(config.get('insecureTLS',False)) mqtt_client.tls_insecure_set(config.get('insecureTLS',False))
mqtt_client.username_pw_set(config.get('user', None), config.get('password', None)) mqtt_client.username_pw_set(config.get('user', None), config.get('password', None))
last_will = config.get('last_will', None)
if last_will:
lw_topic = last_will.get('topic', 'last will hoymiles')
lw_payload = last_will.get('payload', 'last will')
mqtt_client.will_set(str(lw_topic), str(lw_payload))
mqtt_client.connect(config.get('host', '127.0.0.1'), config.get('port', 1883)) mqtt_client.connect(config.get('host', '127.0.0.1'), config.get('port', 1883))
mqtt_client.loop_start() mqtt_client.loop_start()
self.client = mqtt_client self.client = mqtt_client
self.qos = config.get('QoS', 0) # Quality of Service
self.ret = config.get('Retain', True) # Retain Message
def disco(self, **params):
self.client.loop_stop() # Stop loop
self.client.disconnect() # disconnect
def store_status(self, response, **params): def store_status(self, response, **params):
""" """
@ -202,17 +215,17 @@ class MqttOutputPlugin(OutputPluginFactory):
# Global Head # Global Head
if data['time'] is not None: if data['time'] is not None:
self.client.publish(f'{topic}/time', data['time'].strftime("%d.%m.%y - %H:%M:%S")) self.client.publish(f'{topic}/time', data['time'].strftime("%d.%m.%y - %H:%M:%S"), self.qos, self.ret)
# AC Data # AC Data
phase_id = 0 phase_id = 0
phase_sum_power = 0 phase_sum_power = 0
for phase in data['phases']: for phase in data['phases']:
self.client.publish(f'{topic}/emeter/{phase_id}/voltage', phase['voltage']) self.client.publish(f'{topic}/emeter/{phase_id}/voltage', phase['voltage'], self.qos, self.ret)
self.client.publish(f'{topic}/emeter/{phase_id}/current', phase['current']) self.client.publish(f'{topic}/emeter/{phase_id}/current', phase['current'], self.qos, self.ret)
self.client.publish(f'{topic}/emeter/{phase_id}/power', phase['power']) self.client.publish(f'{topic}/emeter/{phase_id}/power', phase['power'], self.qos, self.ret)
self.client.publish(f'{topic}/emeter/{phase_id}/Q_AC', phase['reactive_power']) self.client.publish(f'{topic}/emeter/{phase_id}/Q_AC', phase['reactive_power'], self.qos, self.ret)
self.client.publish(f'{topic}/emeter/{phase_id}/frequency', phase['frequency']) self.client.publish(f'{topic}/emeter/{phase_id}/frequency', phase['frequency'], self.qos, self.ret)
phase_id = phase_id + 1 phase_id = phase_id + 1
phase_sum_power += phase['power'] phase_sum_power += phase['power']
@ -220,36 +233,38 @@ class MqttOutputPlugin(OutputPluginFactory):
string_id = 0 string_id = 0
string_sum_power = 0 string_sum_power = 0
for string in data['strings']: for string in data['strings']:
self.client.publish(f'{topic}/emeter-dc/{string_id}/voltage', string['voltage']) self.client.publish(f'{topic}/emeter-dc/{string_id}/voltage', string['voltage'], self.qos, self.ret)
self.client.publish(f'{topic}/emeter-dc/{string_id}/current', string['current']) self.client.publish(f'{topic}/emeter-dc/{string_id}/current', string['current'], self.qos, self.ret)
self.client.publish(f'{topic}/emeter-dc/{string_id}/power', string['power']) self.client.publish(f'{topic}/emeter-dc/{string_id}/power', string['power'], self.qos, self.ret)
self.client.publish(f'{topic}/emeter-dc/{string_id}/YieldDay', string['energy_daily']) self.client.publish(f'{topic}/emeter-dc/{string_id}/YieldDay', string['energy_daily'], self.qos, self.ret)
self.client.publish(f'{topic}/emeter-dc/{string_id}/YieldTotal', string['energy_total']/1000) self.client.publish(f'{topic}/emeter-dc/{string_id}/YieldTotal', string['energy_total']/1000, self.qos, self.ret)
self.client.publish(f'{topic}/emeter-dc/{string_id}/Irradiation', string['irradiation']) self.client.publish(f'{topic}/emeter-dc/{string_id}/Irradiation', string['irradiation'], self.qos, self.ret)
string_id = string_id + 1 string_id = string_id + 1
string_sum_power += string['power'] string_sum_power += string['power']
# Global # Global
if data['event_count'] is not None: if data['event_count'] is not None:
self.client.publish(f'{topic}/total_events', data['event_count']) self.client.publish(f'{topic}/total_events', data['event_count'], self.qos, self.ret)
if data['powerfactor'] is not None: if data['powerfactor'] is not None:
self.client.publish(f'{topic}/PF_AC', data['powerfactor']) self.client.publish(f'{topic}/PF_AC', data['powerfactor'], self.qos, self.ret)
self.client.publish(f'{topic}/Temp', data['temperature']) self.client.publish(f'{topic}/Temp', data['temperature'], self.qos, self.ret)
if data['yield_total'] is not None: if data['yield_total'] is not None:
self.client.publish(f'{topic}/YieldTotal', data['yield_total']/1000) self.client.publish(f'{topic}/YieldTotal', data['yield_total']/1000, self.qos, self.ret)
if data['yield_today'] is not None: if data['yield_today'] is not None:
self.client.publish(f'{topic}/YieldToday', data['yield_today']/1000) self.client.publish(f'{topic}/YieldToday', data['yield_today']/1000, self.qos, self.ret)
self.client.publish(f'{topic}/Efficiency', data['efficiency']) self.client.publish(f'{topic}/Efficiency', data['efficiency'], self.qos, self.ret)
elif isinstance(response, HardwareInfoResponse): elif isinstance(response, HardwareInfoResponse):
self.client.publish(f'{topic}/Firmware/Version',\ self.client.publish(f'{topic}/Firmware/Version',\
f'{data["FW_ver_maj"]}.{data["FW_ver_min"]}.{data["FW_ver_pat"]}') f'{data["FW_ver_maj"]}.{data["FW_ver_min"]}.{data["FW_ver_pat"]}', self.qos, self.ret)
self.client.publish(f'{topic}/Firmware/Build_at',\ self.client.publish(f'{topic}/Firmware/Build_at',\
f'{data["FW_build_dd"]}/{data["FW_build_mm"]}/{data["FW_build_yy"]}T{data["FW_build_HH"]}:{data["FW_build_MM"]}') f'{data["FW_build_dd"]}/{data["FW_build_mm"]}/{data["FW_build_yy"]}T{data["FW_build_HH"]}:{data["FW_build_MM"]}',\
self.qos, self.ret)
self.client.publish(f'{topic}/Firmware/HWPartId', f'{data["FW_HW_ID"]}') self.client.publish(f'{topic}/Firmware/HWPartId',\
f'{data["FW_HW_ID"]}', self.qos, self.ret)
else: else:
raise ValueError('Data needs to be instance of StatusResponse or a instance of HardwareInfoResponse') raise ValueError('Data needs to be instance of StatusResponse or a instance of HardwareInfoResponse')

Loading…
Cancel
Save