#!/usr/bin/python3 # vim: set fileencoding=utf-8 : # # Munin plugin to monitor the temperature values # of a pellematic/pelletronic touch 2.0 # # Copyright 2013 Guido Günther # # 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 2 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # depends: python-requests # # FIXME: This currently doesn't probe the connected systems # #%# capabilities=autoconf #%# family=contrib import requests import os import re import sys import json username = os.getenv('username', 'oekofen') password = os.getenv('password', username) debug = os.getenv('debug') url = os.getenv('url') # The items we want to plot: items = {# Outside temperature sensor (X2) "CAPPL:LOCAL.L_aussentemperatur_ist": "Outside Temp Act", # 1. Pelletronic "CAPPL:FA[0].L_kesseltemperatur": "Boiler Temp", "CAPPL:FA[0].L_kesseltemperatur_soll_anzeige": "Boiler Temp Set", #"CAPPL:FA[0].L_feuerraumtemperatur", #"CAPPL:FA[0].L_feuerraumtemperatur_soll", # 1. Buffer # TPM (X8) "CAPPL:LOCAL.L_pu[0].ausschaltfuehler_ist": "TPM Act", "CAPPL:LOCAL.L_pu[0].ausschaltfuehler_soll": "TPM Set", # TPO (X7) "CAPPL:LOCAL.L_pu[0].einschaltfuehler_ist": "TPO Act", "CAPPL:LOCAL.L_pu[0].einschaltfuehler_soll": "TPO Set", # 1. Heating circuit # VL1 (X4) "CAPPL:LOCAL.L_hk[0].vorlauftemp_ist": "Flow Temp Act", "CAPPL:LOCAL.L_hk[0].vorlauftemp_soll": "Flow Temp Set", "CAPPL:LOCAL.hk[0].raumtemp_heizen": "Room Temp Heating", "CAPPL:LOCAL.hk[0].raumtemp_absenken": "Room Temp Set Back Set", "CAPPL:LOCAL.hk[0].heizgrenze_heizen": "H Limit Heating", "CAPPL:LOCAL.hk[0].heizgrenze_absenken": "H Limit Set Back", # drinking water "CAPPL:LOCAL.L_ww[0].einschaltfuehler_ist": "Switch On Sensor Act", } def fixup(name): # TPM and TPO are swapped in the returned JSON # (compare to /js/config.min.js) so we need # to fix it up: if name.startswith('TPM'): return 'TPO' + name[3:] elif name.startswith('TPO'): return 'TPM' + name[3:] else: return name def canon(name): return re.sub(r"[^a-zA-Z0-9_]", "_", name) def is_preset(name): """ Preset values are inconsistently named so so we have to decide manually. """ if name.endswith(' Set'): return True elif ' Limit ' in name: return True elif name == 'Room Temp Heating': return True else: return False def print_config(url): print("""graph_title Pellematic Temperature values graph_vlabel Temperature in Degree Celsius graph_category Heating graph_info This graph shows different temperture values as displayed by a Pelletronic Touch 2.0 by Ökofen""") out = fetch_raw(url) names = [] for item in out: if item['name'] in list(items.keys()): names.append(items[item['name']]) for name in sorted(names): print("%s.label %s" % (canon(name), name)) print("%s.type GAUGE" % canon(name)) thickness = 1 if is_preset(name) else 2 print("%s.draw LINE%d" % (canon(name), thickness)) def degree_celsius(val): return float(int(val['value'])) / float(val['divisor']) def fetch_raw(url): # Perform authentication and get the cookie auth_formdata = {'language': 'en', 'username': username, 'password': password, 'submit': 'Login', } r = requests.post(url, data=auth_formdata) pksession = r.cookies['pksession'] cookies = { 'pksession': pksession, 'language': 'en' } params = { 'action': 'get', 'attr': '1', } # These are the headers used by the supplied application # It mostly doesn't matter what we pass but the backend # breaks with a lua error without a Accept-Language header: headers = {'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Accept-Language': 'en', } # The items to fetch are passed as simple json string payload = json.dumps(list(items.keys())) r = requests.post(url, data=payload, params=params, cookies=cookies, headers=headers) ret = r.json() if debug: print(ret, file=sys.stderr) return ret def fetch_values(url): out = fetch_raw(url) for item in out: if item['name'] in items: name = canon(items[item['name']]) print("%s.value %.2f" % (name, degree_celsius(item))) def main(args): if not url: print("No url configured", file=sys.stderr) return 1 if len(args) > 1: if args[1] in [ 'autoconf', 'detect' ]: try: fetch_raw(url) print("yes") return 0 except: print("no") return 1 elif args[1] == 'config': try: print_config(url) except Exception as e: print("Failed to fetch config: '%s'" % e, file=sys.stderr) return 1 return 0 try: fetch_values(url) except Exception as e: print("Failed to fetch values: '%s'" % e, file=sys.stderr) return 1 return 0 if __name__ == "__main__": sys.exit(main(sys.argv))