From 91d682cd97d09dd19eb2e804c670939fe0b29163 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Sun, 19 Jan 2020 20:10:07 +0100 Subject: [PATCH] First version of collectd plugin --- README.md | 48 +++++++++++++++- collectd/collectd_sem6000.py | 108 +++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100755 collectd/collectd_sem6000.py diff --git a/README.md b/README.md index 9929704..5333571 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# SEM6000 Python Library + +SEM6000 is a energy meter and power switch with Bluetooth 4.0. + +This library provides a Python module for these devices + +## Run the example code + ``` $ git clone … sem6000 $ cd sem6000 @@ -5,4 +13,42 @@ $ virtualenv -p python3 python3_venv $ . ./python3_venv/bin/activate $ pip3 install -r requirements.txt $ python3 example.py -``` \ No newline at end of file +``` + +## Collectd Plugin + +You can find a Plugin for [collectd](https://collectd.org) in the `collectd` +subdirectory. + +Installation procedure (the target directory may be changed of course): + +```shell +# mkdir -p /usr/local/lib/collectd/python +# cp collectd/collectd_sem6000.py /usr/local/lib/collectd/python +# cp sem6000.py /usr/local/lib/collectd/python +``` + +Add or adjust the configuration for your collectd’s Python plugin as follows: + +``` + + ModulePath "/usr/local/share/collectd/python" + LogTraces true + Interactive false + Import "collectd_sem6000" + + + Address "12:34:56:78:90:ab" + SocketName "FirstSocket" + + + Address "ab:cd:ef:13:37:42" + SocketName "ASecondSocket" + + # ... + +``` + +Make sure that everything listed in `requirements.txt` is available to the user +running collectd. + diff --git a/collectd/collectd_sem6000.py b/collectd/collectd_sem6000.py new file mode 100755 index 0000000..76f19bb --- /dev/null +++ b/collectd/collectd_sem6000.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# coding: utf-8 +# vim: noet ts=2 sw=2 sts=2 + +import os +import collectd + +from sem6000 import SEMSocket +import bluepy + +instances = [] + +def init_func(): + pass + +def config_func(cfg): + global instances + + config = {} + + for node in cfg.children: + key = node.key.lower() + value = node.values[0] + + if key in ['address', 'socketname']: + config[key] = value + + if 'address' not in config.keys(): + collectd.error('sem6000: address must be set') + return + + if 'socketname' not in config.keys(): + config['socketname'] = config['address'].replace(':', '') + + instances.append( {'config': config, 'socket': None} ) + +def read_func(): + global instances + + for inst in instances: + config = inst['config'] + + try: + if inst['socket'] == None: + collectd.info("sem6000: Connecting to {}...".format(config['address'])) + + inst['socket'] = SEMSocket(config['address']) + collectd.info("sem6000: Connected.") + + inst['socket'].getStatus() + except (SEMSocket.NotConnectedException, bluepy.btle.BTLEDisconnectError, BrokenPipeError) as e: + collectd.warning("sem6000: Exception caught: {}".format(e)) + collectd.warning("sem6000: Restarting on next cycle...") + + if inst['socket'] != None: + inst['socket'].disconnect() + inst['socket'] = None + + socket = inst['socket'] + + if socket != None and socket.voltage != 0: + collectd.debug("Uploading values for {}".format(socket.mac_address)) + + val = collectd.Values(plugin = 'sem6000-{}'.format(config['socketname'])) + + val.type = 'voltage' + val.type_instance = 'grid' + val.values = [ socket.voltage ] + val.dispatch() + + val.type = 'current' + val.type_instance = 'load' + val.values = [ socket.current ] + val.dispatch() + + val.type = 'power' + val.type_instance = 'real_power' + val.values = [ socket.power ] + val.dispatch() + + val.type = 'gauge' + val.type_instance = 'power_factor' + val.values = [ socket.power_factor ] + val.dispatch() + + val.type = 'gauge' + val.type_instance = 'load_on' + val.values = [ socket.powered ] + val.dispatch() + + val.type = 'frequency' + val.type_instance = 'grid' + val.values = [ socket.frequency ] + val.dispatch() + +def shutdown_func(): + global instances + + for inst in instances: + if inst['socket'] != None: + inst['socket'].disconnect() + + instances = [] + +collectd.register_config(config_func) +collectd.register_init(init_func) +collectd.register_read(read_func) +collectd.register_shutdown(shutdown_func)