FanLedCtrl/mqttclient/fanATserial2mqtt.py

192 lines
6.9 KiB
Python
Raw Normal View History

2024-12-07 14:25:04 +01:00
#! /usr/bin/python3
import time
import sys
from paho.mqtt import client as mqtt_client
import os
2024-12-07 14:36:47 +01:00
import serial
2024-12-22 20:31:59 +01:00
import logging
2024-12-07 14:36:47 +01:00
MAXIMUM_COMMUNICATION_FAILURE=1000
2024-12-07 15:26:22 +01:00
2024-12-07 15:49:02 +01:00
TEMPERATUR_MIN_DIFFERENCE = 2
TARGET_MIN_DIFFERENCE = 1
RPM_MIN_DIFFERENCE = 50
2024-12-22 20:31:59 +01:00
COMMAND_PREFIX = "ollpe"
2024-12-07 14:36:47 +01:00
# MQTT Settings
2024-12-07 14:25:04 +01:00
mqtt_server=os.environ['MQTT_SERVER']
mqtt_topic=os.environ['MQTT_TOPIC']
2024-12-07 14:36:47 +01:00
# SERIAL Port
serial_device=os.environ['SERIAL_DEVICE']
2024-12-07 14:25:04 +01:00
2024-12-22 20:31:59 +01:00
# Logging
try:
if (os.environ['PY_LOGGING']):
customLevel = os.environ['PY_LOGGING'].upper()
except (KeyError):
customLevel = 'WARNING'
logging.basicConfig(
level=customLevel,
filename="/var/log/app.log",
filemode="w",
format="%(asctime)s - %(levelname)s - %(message)s"
)
logging.debug("Start Serial connect")
2024-12-07 15:49:02 +01:00
# Start Serial communication
ser = serial.Serial(serial_device, 115200, timeout=1) # open serial port
2024-12-07 15:33:16 +01:00
2024-12-22 20:31:59 +01:00
if (ser is None):
logging.error("Cannot open device" + str(serial_device))
sys.exit()
2024-12-07 15:49:02 +01:00
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
2024-12-07 16:23:36 +01:00
payload = msg.payload.decode("utf-8")
if ((mqtt_topic + "cmd/rpm") == msg.topic):
if (ser.is_open):
2024-12-22 20:31:59 +01:00
command = COMMAND_PREFIX+"f{:02x}".format(int(payload))
2024-12-07 16:23:36 +01:00
ser.write(command.encode('utf-8'))
else:
client.publish(mqtt_topic + "cmd/exception", "Serial Connection closed") # clear exception
elif ((mqtt_topic + "cmd/led") == msg.topic):
2024-12-22 20:31:59 +01:00
command = COMMAND_PREFIX+"l" + payload
2024-12-07 16:23:36 +01:00
ser.write(command.encode('utf-8'))
else:
2024-12-22 20:31:59 +01:00
logging.warning(msg.topic+" "+str(msg.payload))
2024-12-07 15:33:16 +01:00
2024-12-07 15:49:02 +01:00
# Handle MQTT Commands
2024-12-07 16:23:36 +01:00
def on_connect(client, obj, flags, reason_code, properties):
2024-12-07 15:33:16 +01:00
client.subscribe(mqtt_topic + "cmd/led")
client.subscribe(mqtt_topic + "cmd/rpm")
2024-12-22 20:31:59 +01:00
client.publish(mqtt_topic + "online", payload="true", qos=1, retain=True)
logging.debug("Connected: " + str(reason_code))
2024-12-07 15:33:16 +01:00
2024-12-07 16:23:36 +01:00
# Start mqtt
client_id = f'python-fan-ctrl'
client = mqtt_client.Client(mqtt_client.CallbackAPIVersion.VERSION2)
2024-12-07 14:25:04 +01:00
client.on_message = on_message
2024-12-07 15:33:16 +01:00
client.on_connect = on_connect
2024-12-07 14:25:04 +01:00
if (not (mqtt_server)):
2024-12-22 20:31:59 +01:00
logging.error("MQTT_SERVER is not set")
2024-12-07 14:25:04 +01:00
if (not (mqtt_topic)):
2024-12-22 20:31:59 +01:00
logging.error("MQTT_TOPIC is not set")
2024-12-07 14:25:04 +01:00
2024-12-07 15:26:22 +01:00
# Start MQTT Client
2024-12-22 20:31:59 +01:00
client.will_set(mqtt_topic + "online", payload="false", qos=1, retain=True)
2024-12-07 14:25:04 +01:00
client.connect(mqtt_server, 1883)
client.loop_start()
2024-12-07 14:36:47 +01:00
client.publish(mqtt_topic + "device", ser.name) # check which port was really used
# Reset Serial Communication statistic
2024-12-07 15:26:22 +01:00
SerialCommunicationFailureCounter=0
client.publish(mqtt_topic + "serialcom/failure", str(SerialCommunicationFailureCounter))
2024-12-07 14:36:47 +01:00
2024-12-07 15:49:02 +01:00
# store last published value
lastTemperatur = -1
lastRPM = -1
lastTarget = -1
2024-12-07 14:25:04 +01:00
# Endless Loop
2024-12-07 15:49:02 +01:00
try:
2024-12-22 20:31:59 +01:00
while ((mqtt_topic is not None) and (mqtt_server is not None)):
2024-12-07 15:26:22 +01:00
try:
if (not (ser.is_open)):
ser.open()
continue
except (serial.SerialException):
2024-12-22 20:31:59 +01:00
logging.error("Could not open serial connection")
2024-12-07 15:26:22 +01:00
client.publish(mqtt_topic + "exception", "serial reopen") # check which port was really used
2024-12-17 20:58:17 +01:00
time.sleep(3.0)
2024-12-07 15:26:22 +01:00
try:
line = ser.readline() # read a '\n' terminated line
except (serial.SerialException):
SerialCommunicationFailureCounter = SerialCommunicationFailureCounter + 1
client.publish(mqtt_topic + "serialcom/failure", str(SerialCommunicationFailureCounter))
line=None
if (SerialCommunicationFailureCounter > MAXIMUM_COMMUNICATION_FAILURE):
raise serial.SerialException("device reports readiness / device disconnected " + str(MAXIMUM_COMMUNICATION_FAILURE) + " times")
2024-12-22 20:31:59 +01:00
if (line) :
2024-12-07 15:26:22 +01:00
client.publish(mqtt_topic + "exception", "") # clear exception
2024-12-22 20:31:59 +01:00
# activate debug code, by default
temperatur = None
percent = None
rpm = None
2024-12-07 15:26:22 +01:00
# convert byte array to string
l = line.decode("utf-8")
2024-12-22 20:31:59 +01:00
if (COMMAND_PREFIX in l):
logging.debug("Skip '" + str(l) + "', found " + str(COMMAND_PREFIX))
continue
2024-12-07 15:26:22 +01:00
try:
# Parse the controller information
# e.g. : 22.00;0%;0RPM
temperatur, percent, rpm = l.split(";")
except (ValueError):
2024-12-22 20:31:59 +01:00
logging.debug("Unparsable '" + str(l) + "'")
2024-12-07 15:26:22 +01:00
if ((temperatur) and (percent) and (rpm)):
parseError=False
2024-12-07 16:23:36 +01:00
try:
currentTemperatur = float(temperatur)
except (ValueError):
2024-12-22 20:31:59 +01:00
logging.error("Temperatur Error: could not convert string to float:" + temperatur)
2024-12-07 16:23:36 +01:00
currentTemperatur = lastTemperatur
2024-12-07 15:49:02 +01:00
if (abs(currentTemperatur - lastTemperatur) > TEMPERATUR_MIN_DIFFERENCE):
client.publish(mqtt_topic + "temperatur", str(currentTemperatur))
lastTemperatur = currentTemperatur
2024-12-07 15:26:22 +01:00
if (percent.endswith('/100')):
2024-12-07 16:23:36 +01:00
try:
currentTarget = int(percent[:-4])
except (ValueError):
2024-12-22 20:31:59 +01:00
logging.error("Percent Error: could not convert string to int:" + (percent[:-4]))
2024-12-07 16:23:36 +01:00
currentTarget = lastTarget
2024-12-07 15:49:02 +01:00
if (abs(lastTarget - currentTarget) > TARGET_MIN_DIFFERENCE):
client.publish(mqtt_topic + "target", str(currentTarget))
lastTarget = currentTarget
2024-12-07 15:26:22 +01:00
else:
client.publish(mqtt_topic + "debug", "target%% invalid:" + str(percent))
parseError=True
# remove new line for last element
rpm = rpm.replace('\r','').replace('\n','')
if (rpm.endswith('RPM')):
2024-12-07 16:23:36 +01:00
try:
currentRPM = int(rpm[:-3])
except (ValueError):
2024-12-22 20:31:59 +01:00
logging.error("RPM Error: could not convert string to int:" + (rpm[:-3]))
2024-12-07 16:23:36 +01:00
currentRPM = lastRPM
2024-12-07 15:49:02 +01:00
if (abs(currentRPM-lastRPM) > RPM_MIN_DIFFERENCE):
client.publish(mqtt_topic + "rpm", str(currentRPM))
lastRPM = currentRPM
2024-12-07 15:26:22 +01:00
else:
client.publish(mqtt_topic + "debug", "RPM invalid:" + str(rpm))
parseError=True
2024-12-07 14:25:04 +01:00
2024-12-07 15:26:22 +01:00
# cleanup debugging
if (parseError == False):
client.publish(mqtt_topic + "debug", None)
else:
client.publish(mqtt_topic + "debug", line) # line was invalid and could not be parsed
else:
2024-12-17 20:58:17 +01:00
time.sleep(0.5)
2024-12-07 14:25:04 +01:00
2024-12-07 15:49:02 +01:00
except (KeyboardInterrupt, SystemExit):
2024-12-22 20:31:59 +01:00
logging.error("User aborted service ...")
2024-12-07 15:49:02 +01:00
client.publish(mqtt_topic + "debug", "User aborted service")
time.sleep(0.5)
2024-12-22 20:31:59 +01:00
logging.debug("service Dead")
2024-12-07 14:25:04 +01:00
client.loop_stop()