commit dba0ef4a4e7fa112831e7ee3b4198f6c09fa7fc0 Author: Anna Christina Naß Date: Tue Jul 11 14:29:20 2017 +0200 Erste Fassung diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e499d2 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# fritz_temperatur + +## Allgemein + +Dieses Script fragt die Temperatur eines an die FritzBox angeschlossenen Sensors ab. + +Getestet wurde es mit einer FritzBox 7390 mit FritzOS 6.83 und einem Comet DECT-Heizkörperregler. + +Das Script benötigt folgende Parameter: + + -H Hostname oder IP der FritzBox + -U Benutzername zum Einloggen an der FritzBox (z.B. administrator) + -P Das Paßwort des eben genannten Benutzers + -A Die ID des Sensors. + +Die Sensor-ID kann man über die Web-Oberfläche der FritzBox abfragen: +- Menü Heimnetz => Smart Home +- Beim gewünschten Sensor auf "Bearbeiten" (Stift-Symbol) klicken +- Die "Identifikationsnummer" ist die benötigte ID. Leerzeichen aus der ID entfernen. + +## Voraussetzungen + +Das Script ist in Python3 geschrieben und benötigt die folgenden Debian-Pakete: + +* python3 +* python3-urllib + +Getestet wurde mit Debian 8 (Jessie). + diff --git a/fritz_temp b/fritz_temp new file mode 100755 index 0000000..4735d25 --- /dev/null +++ b/fritz_temp @@ -0,0 +1,127 @@ +#!/usr/bin/python3 +# get target temperature from AVM/Comet Dect Heizkoerperregler (Heating Device) +# need Fritz!OS 6.50+ + +import getopt +import re +import requests +import sys +import xml.etree.ElementTree as etree +import urllib.parse +from hashlib import md5 + +DEBUG=False + +def OK(text): + print("OK: " + text) + sys.exit() + +def Warning(text): + print("WARNING: " + text) + sys.exit() + +def Critical(text): + print("CRITICAL: " + text) + sys.exit() + +def OptionsError(): + Critical("Error: not all required options are given.\n -H \n -U \n -P \n -A ") + +def debug(text): + if DEBUG == True: + print("DEBUG: " + text) + +def get_sid(loginurl, username, password): + if not username: + username='' + + # get challenge string + response = requests.get(loginurl) + + if response.status_code == 200: + tree = etree.fromstring(response.content) + challenge = tree.find("Challenge").text + sid = tree.find("SID").text + debug("challenge: " + challenge) + debug("sid: " + sid) + else: + Critical("Error: GetSid failed") + + if len(sid) > 0 and re.search("^0+$", sid) and challenge: + debug("Get new SID") + # sid is null, got challenge + sid = "" + # build password response + pwd = challenge + "-" + password + # UTF-16LE encoding as required + pwd = pwd.encode("UTF-16LE") + # md5hash on top + m = md5() + m.update(pwd) + # final answer string + challenge_response = challenge + "-" + m.hexdigest() + debug("challenge_response: " + challenge_response) + # send to box + url = loginurl + "?username=" + username + "&response=" + challenge_response + response = requests.get(url) + #print(response.content) + # check answer + tree = etree.fromstring(response.content) + sid2 = tree.find("SID").text + debug("SID: " + sid2) + if (len(sid2) > 0) and not re.search("^0+$", sid2): + # is not null, bingo! + return sid2 + else: + # use existing sid if sid matches an hex string + if (len(sid) > 0) and re.search("^[0-9a-f]+$", sid): + return sid + +host = None +username = None +password = None +ain = None + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], 'H:U:P:A:') +except getopt.GetoptError as err: + OptionsError() + +for opt, arg in opts: + if opt == "-H": + host = arg + elif opt == "-U": + username = arg + elif opt == "-P": + password = arg + elif opt == "-A": + ain = arg + +if not host or not username or not password or not ain: + OptionsError() + +# build urls and login +# - login_sid.lua for fritzos 5.50+ +loginurl = "http://" + host + "/login_sid.lua" +ahaurl = "http://" + host + "/webservices/homeautoswitch.lua" +sid = get_sid(loginurl, username, password); +if not sid: + Critical("Error: Login invalid or failed") + +#query actual state +rain = urllib.parse.quote(ain) +url = ahaurl + '?ain=' + rain + '&sid=' + sid + '&switchcmd=gettemperature'; + +http_response = requests.get(url) + +if http_response.status_code == 200: + # check answer + answer = http_response.content.decode().strip() + debug("Antwort: " + answer) + temperatur = float(answer) / 10 + temp_str = "{0:<3.1f}".format(temperatur) + OK("Temperatur: " + temp_str + " | temp=" + temp_str) +else: + Critical("Error: Query failed.") + +