This commit is contained in:
Wolfgang Hottgenroth 2022-11-20 18:44:48 +01:00
commit f30c8e7eb3
Signed by: wn
GPG Key ID: 836E9E1192A6B132
9 changed files with 217 additions and 0 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Wolfgang Hottgenroth
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Binary file not shown.

26
pyproject.toml Normal file
View File

@ -0,0 +1,26 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "pv-controller"
version = "0.0.1"
authors = [
{ name="Wolfgang Hottgenroth", email="wolfgang.hottgenroth@icloud.com" },
]
description = "Special tool to control my PV installation"
readme = "README.md"
license = { file="LICENSE" }
requires-python = ">=3.10"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"loguru>=0.6.0"
]
[project.urls]
"Homepage" = "https://home.hottis.de/gitlab/wolutator/pv-controller"
"Bug Tracker" = "https://home.hottis.de/gitlab/wolutator/pv-controller/-/issues"

0
readme.md Normal file
View File

View File

@ -0,0 +1,75 @@
import paho.mqtt.client as mqtt
from loguru import logger
import threading
import ssl
def mqttOnConnectCallback(client, userdata, flags, rc):
userdata.onConnect()
def mqttOnMessageCallback(client, userdata, message):
userdata.onMessage(message.topic, message.payload)
def mqttOnDisconnectCallback(client, userdata, rc):
userdata.onDisconnect(rc)
class AbstractMqttPublisher(threading.Thread):
def __init__(self, config):
super().__init__()
self.config = config["mqtt"]
self.client = mqtt.Client(userdata=self)
# consider this flag in the localLoop
self.killBill = False
self.killEvent = threading.Event()
def run(self):
self.client.on_message = mqttOnMessageCallback
self.client.on_connect = mqttOnConnectCallback
self.client.on_disconnect = mqttOnDisconnectCallback
if ("login" in self.config) and ("password" in self.config):
self.client.username_pw_set(self.config["login"], self.config["password"])
if ("ca" in self.config) and ("cert" in self.config) and ("key" in self.config):
self.client.tls_set(
ca_certs=self.config["ca"],
certfile=self.config["cert"],
keyfile=self.config["key"],
cert_reqs=ssl.CERT_REQUIRED,
tls_version=ssl.PROTOCOL_TLSv1_2,
ciphers=None # this does not mean "no cipher" but it means "default ciphers"
)
self.client.connect(self.config["broker"], int(self.config["port"]))
self.client.loop_start()
logger.info("mqtt loop started")
self.localLoop()
def localLoop(self):
raise NotImplementedError()
def stop(self):
self.client.loop_stop()
logger.info("mqtt loop stopped")
self.killBill = True
logger.info("kill flag set")
self.killEvent.set()
logger.info("kill events triggered")
def onConnect(self):
logger.info("mqtt connected")
def onDisconnect(self, rc):
logger.warning("mqtt disconnect, rc: {}".format(rc))
def onMessage(self, topic, payload):
logger.warning("mqtt unexpected message received: {} -> {}".format(topic, str(payload)))

View File

@ -0,0 +1,19 @@
from threading import Event
from loguru import logger
from MqttBase import AbstractMqttPublisher
class TestPublish(AbstractMqttPublisher):
def __init__(self, config):
super().__init__(config)
def localLoop(self):
cnt = 0
while not self.killBill:
cnt += 1
topic = self.config["publishTopic"]
payload = str(cnt)
self.client.publish(topic, payload)
logger.warning("mqtt message sent: {} -> {}".format(topic, payload))
self.killEvent.wait(timeout=float(self.config["publishPeriod"]))

View File

@ -0,0 +1,20 @@
from MqttBase import AbstractMqttPublisher
from loguru import logger
from time import sleep
class TestSubscribe(AbstractMqttPublisher):
def __init__(self, config):
super().__init__(config)
def localLoop(self):
while not self.killBill:
sleep(60.0)
def onMessage(self, topic, payload):
logger.warning("mqtt message received: {} -> {}".format(topic, str(payload)))
def onConnect(self):
logger.info("mqtt connected")
self.client.subscribe("{}".format(self.config["subscribeTopic"]))
logger.info("subscribed")

View File

View File

@ -0,0 +1,56 @@
from TestPublish import TestPublish
from TestSubscribe import TestSubscribe
from loguru import logger
import argparse
import configparser
import threading
deathBell = threading.Event()
def exceptHook(args):
global deathBell
logger.error("Exception in thread caught: {}".format(args))
deathBell.set()
logger.error("rang the death bell")
logger.info("example1 starting")
parser = argparse.ArgumentParser(description="example1")
parser.add_argument('--config', '-f',
help='Config file, default is $pwd/config.ini',
required=False,
default='./config.ini')
args = parser.parse_args()
config = configparser.ConfigParser()
config.read(args.config)
testSubscribeThread = TestSubscribe(config)
testSubscribeThread.start()
logger.info("testSubscribe started")
testPublishThread = TestPublish(config)
testPublishThread.start()
logger.info("testPublish started")
threading.excepthook = exceptHook
logger.info("Threading excepthook set")
logger.info("example1 is running")
deathBell.wait()
logger.error("example1 is dying")
testSubscribeThread.stop()
testPublishThread.stop()
testSubscribeThread.join()
logger.error("testSubscribe joined")
testPublishThread.join()
logger.error("testPublish joined")
logger.error("example1 is terminated")