2015-04-05 13:03:56 +02:00
|
|
|
#!/usr/bin/env python3
|
2015-03-08 16:24:31 +01:00
|
|
|
|
2015-04-05 13:03:56 +02:00
|
|
|
from select import poll, POLLPRI, POLLIN, POLLERR
|
2015-03-24 10:56:28 +01:00
|
|
|
import sys
|
2015-04-05 13:03:56 +02:00
|
|
|
import os
|
2015-03-24 10:56:28 +01:00
|
|
|
import threading
|
2015-04-05 13:03:56 +02:00
|
|
|
import time
|
|
|
|
import queue
|
|
|
|
import requests
|
2015-05-02 11:16:56 +02:00
|
|
|
import configparser
|
2017-09-05 09:14:13 +02:00
|
|
|
import logging
|
2017-10-02 12:42:33 +02:00
|
|
|
import paho.mqtt.publish as mqtt
|
2015-03-24 10:56:28 +01:00
|
|
|
|
2017-10-02 12:42:33 +02:00
|
|
|
def trans_http():
|
2015-05-02 11:16:56 +02:00
|
|
|
global serverconf
|
|
|
|
# build together url and headers
|
|
|
|
uri = ""
|
|
|
|
if serverconf.getboolean("ssl") == True:
|
|
|
|
uri = "https://"
|
|
|
|
else:
|
|
|
|
uri = "http://"
|
|
|
|
uri = uri + serverconf["url"] + "/get.php"
|
2017-09-05 09:14:13 +02:00
|
|
|
log.info('Remote URI: {}'.format(uri))
|
2015-04-05 13:03:56 +02:00
|
|
|
queuelast = time.time()
|
2015-03-24 10:56:28 +01:00
|
|
|
while True:
|
2015-04-09 23:28:28 +02:00
|
|
|
# get value from queue (blocking)
|
2015-04-05 13:03:56 +02:00
|
|
|
queuedata = powerqueue.get()
|
2015-04-09 23:28:28 +02:00
|
|
|
powerqueue.task_done()
|
2015-04-05 13:17:50 +02:00
|
|
|
queuetime = int(queuedata[0])
|
2015-04-05 13:03:56 +02:00
|
|
|
queueval = str(queuedata[1])
|
2015-04-09 23:28:28 +02:00
|
|
|
# if more than five seconds passed since last event
|
2015-04-05 13:03:56 +02:00
|
|
|
if ((queuetime - queuelast) >= 5):
|
2015-04-09 23:28:28 +02:00
|
|
|
# put payload together
|
2017-10-02 16:05:06 +02:00
|
|
|
if serverconf.getboolean('timestamp'):
|
|
|
|
payload = {"val": str(queuetime) + ";" + str(queueval)}
|
|
|
|
else:
|
|
|
|
payload = {"val": str(queueval)}
|
2017-10-02 12:42:33 +02:00
|
|
|
log.debug('request {}:{}{} => {}'.format(mqttconf['host'], mqttconf['port'], mqttconf['topic'], repr(payload)))
|
|
|
|
# send to http server
|
2015-04-09 23:28:28 +02:00
|
|
|
try:
|
2015-05-02 11:16:56 +02:00
|
|
|
if serverconf.getboolean("basicauth") == True:
|
|
|
|
r = requests.post(uri, data=payload, verify=False, auth=(serverconf["user"], serverconf["password"]), timeout=10, headers={'connection':'close'})
|
|
|
|
else:
|
|
|
|
r = requests.post(uri, data=payload, verify=False, timeout=10, headers={'connection':'close'})
|
2015-04-09 23:28:28 +02:00
|
|
|
except:
|
2017-09-05 09:14:13 +02:00
|
|
|
log.error('server response: {}'.format(str(r.status_code)))
|
2015-04-05 13:03:56 +02:00
|
|
|
queuelast = queuetime
|
2015-04-09 23:28:28 +02:00
|
|
|
|
|
|
|
else:
|
|
|
|
# drop data if less than five seconds passed since last event
|
|
|
|
del queuedata
|
2017-09-05 09:14:13 +02:00
|
|
|
log.info('dropped queued element')
|
2015-03-24 10:56:28 +01:00
|
|
|
|
2017-10-02 12:42:33 +02:00
|
|
|
|
|
|
|
def trans_mqtt():
|
|
|
|
global mqttconf
|
|
|
|
queuelast = time.time()
|
|
|
|
log.info('sending data via mqtt host: {}, port: {}, topic:{}'.format(mqttconf['host'], mqttconf['port'], mqttconf['topic']))
|
|
|
|
while True:
|
|
|
|
# get value from queue (blocking)
|
|
|
|
queuedata = powerqueue.get()
|
|
|
|
powerqueue.task_done()
|
|
|
|
queuetime = int(queuedata[0])
|
|
|
|
queueval = str(queuedata[1])
|
|
|
|
# if more than five seconds passed since last event
|
|
|
|
if ((queuetime - queuelast) >= 5):
|
|
|
|
# put payload together
|
2017-10-02 16:05:06 +02:00
|
|
|
if mqttconf.getboolean('timestamp'):
|
|
|
|
payload = str(queuetime) + ";" + str(queueval)
|
|
|
|
else:
|
|
|
|
payload = str(queueval)
|
2017-10-02 13:59:17 +02:00
|
|
|
log.debug('mqtt {} => {}'.format(mqttconf['host'], repr(payload)))
|
2017-10-02 12:42:33 +02:00
|
|
|
# send to mqtt broker
|
|
|
|
try:
|
|
|
|
mqtt.single(mqttconf['topic'], payload=payload, retain=True, hostname=mqttconf['host'],
|
|
|
|
port=mqttconf.getint('port'), client_id=mqttconf['clientid'], transport="tcp")
|
|
|
|
except BaseException as e:
|
|
|
|
log.error('error while publishing: {}'.format(e))
|
|
|
|
queuelast = queuetime
|
|
|
|
|
|
|
|
else:
|
|
|
|
# drop data if less than five seconds passed since last event
|
|
|
|
del queuedata
|
|
|
|
log.info('dropped queued element')
|
|
|
|
|
|
|
|
|
2015-04-05 13:03:56 +02:00
|
|
|
def readgpio():
|
2015-05-02 11:16:56 +02:00
|
|
|
global gpiopath
|
|
|
|
global gpioconf
|
|
|
|
global smconf
|
2015-04-09 23:28:28 +02:00
|
|
|
# open gpio filehandle
|
2015-05-02 11:16:56 +02:00
|
|
|
gpio = open(gpiopath + "value", "r")
|
2015-04-09 23:28:28 +02:00
|
|
|
# setup polling
|
2015-04-05 13:03:56 +02:00
|
|
|
gpiopoll = poll()
|
|
|
|
gpiopoll.register(gpio, POLLERR)
|
2015-04-09 23:28:28 +02:00
|
|
|
|
|
|
|
# wait for two interrupts to have a "clean" starting point
|
2017-09-05 09:14:13 +02:00
|
|
|
log.info('waiting for 2 interrupts to get a clean start')
|
2015-04-05 13:03:56 +02:00
|
|
|
gpioevent = gpiopoll.poll()
|
|
|
|
gpio.read()
|
|
|
|
gpio.seek(0)
|
2017-09-05 09:14:13 +02:00
|
|
|
log.info('waiting for 1 interrupt to get a clean start...')
|
2015-04-05 13:03:56 +02:00
|
|
|
gpioevent = gpiopoll.poll()
|
2015-04-09 23:28:28 +02:00
|
|
|
last = time.time()
|
2015-04-05 13:03:56 +02:00
|
|
|
gpio.read()
|
|
|
|
gpio.seek(0)
|
2015-04-09 23:28:28 +02:00
|
|
|
|
|
|
|
# start readgpio mainloop
|
2017-09-05 09:14:13 +02:00
|
|
|
log.info('start readgpio mainloop')
|
2015-04-05 13:03:56 +02:00
|
|
|
while True:
|
|
|
|
gpioevent = gpiopoll.poll()
|
2015-04-05 16:40:30 +02:00
|
|
|
gpioval = gpio.read(1)
|
2015-04-05 13:03:56 +02:00
|
|
|
gpio.seek(0)
|
2015-04-09 23:28:28 +02:00
|
|
|
# check if interrupt was a falling edge (yes, really!)
|
2015-04-05 16:40:30 +02:00
|
|
|
if gpioval == '0':
|
|
|
|
now = time.time()
|
2015-04-09 23:28:28 +02:00
|
|
|
# plausibility check
|
2015-05-02 11:16:56 +02:00
|
|
|
if ((now-last) >= gpioconf.getfloat("mintime")):
|
2015-04-09 23:28:28 +02:00
|
|
|
# calculate current power consumption
|
2015-05-02 11:16:56 +02:00
|
|
|
power = round((3600000/smconf.getint("impkwh")) / (now-last),2)
|
2017-09-05 09:14:13 +02:00
|
|
|
log.info('Current power consumption: {} Watt'.format(str(power)))
|
|
|
|
log.debug('GPIO state: {}'.format(gpioval))
|
2015-04-09 23:28:28 +02:00
|
|
|
# put measured value on queue
|
2015-04-05 16:40:30 +02:00
|
|
|
powerqueue.put([now, power])
|
|
|
|
last = now
|
2015-04-09 23:28:28 +02:00
|
|
|
# error if a tty is connected and a raising edge was triggered
|
2015-04-05 16:40:30 +02:00
|
|
|
else:
|
2017-09-05 09:14:13 +02:00
|
|
|
log.debug('Not a falling edge, ignoring')
|
2015-03-24 10:56:28 +01:00
|
|
|
|
2015-04-09 23:28:28 +02:00
|
|
|
|
2015-04-05 13:03:56 +02:00
|
|
|
if __name__ == "__main__":
|
2015-05-02 11:16:56 +02:00
|
|
|
# read config
|
2017-10-02 16:05:06 +02:00
|
|
|
dirname, filename = os.path.split(os.path.abspath(__file__))
|
2015-05-02 11:16:56 +02:00
|
|
|
conf = configparser.ConfigParser()
|
2017-10-02 16:05:06 +02:00
|
|
|
try:
|
2017-10-02 16:12:13 +02:00
|
|
|
conf.read(os.path.join(dirname, "powerpi.local.conf"))
|
2017-10-02 16:05:06 +02:00
|
|
|
except:
|
2017-10-02 16:12:13 +02:00
|
|
|
conf.read(os.path.join(dirname, "powerpi.conf"))
|
2017-10-02 16:05:06 +02:00
|
|
|
log.warning('using global config. Use powerpi.local.conf instead')
|
2015-05-02 11:16:56 +02:00
|
|
|
conf.sections()
|
2017-10-02 12:42:33 +02:00
|
|
|
transconf = conf['transport']
|
2015-05-02 11:16:56 +02:00
|
|
|
gpioconf = conf['gpio']
|
|
|
|
serverconf = conf['server']
|
|
|
|
smconf = conf['smartmeter']
|
2017-10-02 12:42:33 +02:00
|
|
|
mqttconf = conf['mqtt']
|
2017-09-05 09:14:13 +02:00
|
|
|
# configure logging
|
|
|
|
log = logging.getLogger(__name__)
|
2017-10-02 13:59:17 +02:00
|
|
|
log.setLevel(logging.DEBUG)
|
2017-10-02 16:05:06 +02:00
|
|
|
log.info('Running in path {}'.format(dirname))
|
2017-10-02 12:42:33 +02:00
|
|
|
if serverconf["url"] == "power.example.com" and mqttconf['host'] == "power.example.com":
|
|
|
|
log.critical('Server/Broker -> url still default value')
|
2015-05-02 11:16:56 +02:00
|
|
|
exit(1)
|
|
|
|
gpiopath = "/sys/class/gpio/gpio" + gpioconf["port"] + "/"
|
2015-04-09 23:28:28 +02:00
|
|
|
# initialise gpio interfaces
|
2015-04-05 13:03:56 +02:00
|
|
|
try:
|
2015-05-02 11:16:56 +02:00
|
|
|
if not os.path.exists(gpiopath):
|
|
|
|
# if not already exported, export gpio port
|
2015-04-05 13:03:56 +02:00
|
|
|
gpioinit = open("/sys/class/gpio/export", "w")
|
2015-05-02 11:16:56 +02:00
|
|
|
gpioinit.write(gpioconf["port"] + "\n")
|
2015-04-05 13:03:56 +02:00
|
|
|
gpioinit.close()
|
2015-05-02 11:16:56 +02:00
|
|
|
# set direction of gpio port to "IN"
|
|
|
|
gpiopin = open(gpiopath + "direction", "w")
|
2015-04-05 13:03:56 +02:00
|
|
|
gpiopin.write("in")
|
|
|
|
gpiopin.close()
|
2015-04-09 23:28:28 +02:00
|
|
|
# set trigger to falling edge
|
2015-05-02 11:16:56 +02:00
|
|
|
gpiotype = open(gpiopath + "edge", "w")
|
2015-04-05 13:03:56 +02:00
|
|
|
gpiotype.write("falling")
|
|
|
|
gpiotype.close()
|
|
|
|
except:
|
2017-09-05 09:14:13 +02:00
|
|
|
log.critical('can not initialize gpio interface. aborting.')
|
2015-04-05 13:03:56 +02:00
|
|
|
sys.exit(1)
|
2015-04-09 23:28:28 +02:00
|
|
|
# defining queue for measured values
|
2015-04-05 13:03:56 +02:00
|
|
|
powerqueue = queue.Queue()
|
2015-04-09 23:28:28 +02:00
|
|
|
|
|
|
|
# disable ssl "no verification" warnings
|
2015-05-02 11:16:56 +02:00
|
|
|
if serverconf.getboolean("sslself") == True:
|
|
|
|
requests.packages.urllib3.disable_warnings()
|
2015-03-24 10:56:28 +01:00
|
|
|
|
2015-04-09 23:28:28 +02:00
|
|
|
# initialising and starting threads
|
2015-04-05 13:03:56 +02:00
|
|
|
th1 = threading.Thread(target=readgpio)
|
2017-10-02 12:42:33 +02:00
|
|
|
if transconf['transport'] == 'mqtt':
|
|
|
|
log.info('method of transfer: mqtt')
|
|
|
|
th2 = threading.Thread(target=trans_mqtt)
|
|
|
|
elif transconf['transport'] == 'http':
|
|
|
|
log.info('method of transfer: http')
|
|
|
|
th2 = threading.Thread(target=trans_http)
|
|
|
|
else:
|
|
|
|
log.error('transfer option {} is invalid. Exiting!'.format(transfer['transfer']))
|
|
|
|
sys.exit(1)
|
|
|
|
|
2015-03-24 10:56:28 +01:00
|
|
|
th1.setDaemon(True)
|
|
|
|
th2.setDaemon(True)
|
|
|
|
th1.start()
|
2015-04-05 13:03:56 +02:00
|
|
|
th2.start()
|
2015-03-24 10:56:28 +01:00
|
|
|
|
2015-04-09 23:28:28 +02:00
|
|
|
#busy loop
|
2015-03-24 10:56:28 +01:00
|
|
|
while True:
|
2015-04-05 13:03:56 +02:00
|
|
|
time.sleep(1)
|