arduino-sensors-toolkit/serial2mqtt.py
Julien Riou bb65a7c6b6
Improve parsing when missing values
The sound module may not be available so the parse function fails. This
commit makes the parsing function more generic by giving a list of
metrics to retreive in order.

Signed-off-by: Julien Riou <julien@riou.xyz>
2021-06-08 07:55:11 +02:00

113 lines
3.4 KiB
Python
Executable file

#!/usr/bin/env python3
import argparse
import configparser
import logging
import os
import sys
import time
import paho.mqtt.client as paho
import serial
logger = logging.getLogger(__name__)
def read_serial(interface):
try:
logger.debug('reading serial interface {}'.format(interface))
s = serial.Serial(interface)
s.flushInput()
for i in range(2):
data = s.readline()
if i > 0:
return data
except Exception as err:
logger.warning(str(err))
time.sleep(2)
def parse_sensor_values(data):
"""
Input line: <humidity>,<temperature>,<sound>
Output structure:
{
'humidity': <humidity>,
'temperature': <temperature>,
'sound': <sound>
}
"""
metrics = ['humidity', 'temperature', 'sound']
values = data.decode('utf-8').strip().split(',')
return dict(zip(metrics, values))
def connect_mqtt(config):
"""Connect to MQTT broken from MQTT config and return client"""
mqtt_host = config.get('host', 'localhost')
mqtt_port = int(config.get('port', 1883))
mqtt_username = config.get('username')
mqtt_password = config.get('password')
mqtt_client_id = config.get('client_id', 'serial2mqtt')
client = paho.Client(client_id=mqtt_client_id)
client.on_connect = on_connect
if mqtt_username:
client.username_pw_set(username=mqtt_username, password=mqtt_password)
logging.debug('connecting to MQTT broker at {}:{}'.format(mqtt_host, mqtt_port))
client.connect(mqtt_host, mqtt_port)
return client
def publish(client, topic, value):
logger.debug('publishing to topic "{}" with value "{}"'.format(topic, value))
client.publish(topic, value)
def on_connect(client, userdata, flags, rc):
if rc > 0:
logger.error(paho.connack_string(rc))
else:
logger.info('connected to MQTT broker')
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--config', help='path to configuration file', default='serial2mqtt.ini')
parser.add_argument('-v', '--verbose', dest='loglevel', action='store_const', const=logging.INFO,
help='print more output')
parser.add_argument('-d', '--debug', dest='loglevel', action='store_const', const=logging.DEBUG,
default=logging.WARNING, help='print even more output')
args = parser.parse_args()
logging.basicConfig(format='%(levelname)s: %(message)s', level=args.loglevel)
config = configparser.ConfigParser()
if os.path.isfile(args.config):
config.read(args.config)
mqtt_config = config['mqtt'] if 'mqtt' in config else {}
mqtt_topic_prefix = mqtt_config.get('topic_prefix', 'sensors')
serial_config = config['serial'] if 'serial' in config else {}
try:
client = connect_mqtt(mqtt_config)
except Exception as err:
logger.error('connection to MQTT broker failed: {}'.format(str(err)))
sys.exit(1)
try:
client.loop_start()
while True:
data = read_serial(serial_config.get('interface', '/dev/cuaU0'))
if data:
data = parse_sensor_values(data)
for sensor_name, value in data.items():
topic = '/'.join([mqtt_topic_prefix, sensor_name])
publish(client, topic, value)
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()