diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2a871b3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3-alpine + +ADD steckie.py /opt/steckie/ +ADD config.yml /opt/steckie/ +ADD requirements.txt /opt/steckie + +RUN pip install -r /opt/steckie/requirements.txt + +WORKDIR /opt/steckie +CMD [ "python", "/opt/steckie/steckie.py" ] diff --git a/config.yml.template b/config.yml.template new file mode 100644 index 0000000..08ec8f3 --- /dev/null +++ b/config.yml.template @@ -0,0 +1,12 @@ +update_itvl: 5 + +db: + host: "localhost" + port: 8086 + name: "homelog" + user: "alfred" + pass: "supersecret123" + +devs: + - name: "steckie_fridge" + url: "http://steckie_fridge" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b1612a3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +influxdb +APScheduler +PyYAML diff --git a/steckie.py b/steckie.py new file mode 100755 index 0000000..5367aa0 --- /dev/null +++ b/steckie.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (C) 2019 Hauke Petersen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys +import yaml +import json +import logging +import argparse +from datetime import datetime +import urllib.request +from influxdb import InfluxDBClient +from apscheduler.schedulers.blocking import BlockingScheduler + + +CONFIGFILE = "/opt/steckie/config.yml" +CMD_SENSOR = "cm?cmnd=Status%208" + + +class Steckie: + def __init__(self, cfgfile): + self.cfg = dict() + + # read configuration + try: + with open(cfgfile, 'r', encoding="utf-8") as f: + self.cfg.update(yaml.load(f, Loader=yaml.BaseLoader)) + except: + logging.error("unable to read config file '{}'".format(cfgfile)) + sys.exit(1) + + self.scheduler = BlockingScheduler() + self.db = InfluxDBClient(host=self.cfg['db']['host'], + port=int(self.cfg['db']['port']), + database=self.cfg['db']['name'], + username=self.cfg['db']['user'], + password=self.cfg['db']['pass']) + + def run(self): + self.scheduler.add_job(self.update, 'interval', + seconds=int(self.cfg['update_itvl'])) + self.scheduler.start() + + def update(self): + for dev in self.cfg['devs']: + url = "{}/{}".format(dev['url'], CMD_SENSOR) + data = {} + try: + with urllib.request.urlopen(url) as resp: + data = json.loads(resp.read().decode("utf-8")) + print(data) + except: + logging.warning("unable to read data from '{}'".format(dev['name'])) + continue + + point = [{ + "measurement": "stromie", + "tags": { + "name": dev['name'], + "url": dev['url'], + "type": "tasmota_awp07l", + }, + "time": data['StatusSNS']['Time'], + "fields": { + "voltage": float(data['StatusSNS']['ENERGY']['Voltage']), + "current": float(data['StatusSNS']['ENERGY']['Current']), + "pwr": float(data['StatusSNS']['ENERGY']['ApparentPower']), + "pwr_r": float(data['StatusSNS']['ENERGY']['ReactivePower']), + "factor": float(data['StatusSNS']['ENERGY']['Factor']), + "e_daily": float(data['StatusSNS']['ENERGY']['Today']), + } + }] + + try: + self.db.write_points(point) + except: + logging.warning("{}: unable to write to DB".format(dev['name'])) + + +def main(args): + logging.basicConfig(format='%(asctime)s %(message)s', + level=logging.WARNING) + steckie = Steckie(args.cfgfile) + steckie.run() + + +if __name__ == "__main__": + p = argparse.ArgumentParser() + p.add_argument("cfgfile", default=CONFIGFILE, nargs='?', help="output dump") + args = p.parse_args() + main(args)