diff --git a/Example/PyDect200_Demo.py b/Example/PyDect200_Demo.py index f74f064..435aef5 100755 --- a/Example/PyDect200_Demo.py +++ b/Example/PyDect200_Demo.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -- coding: utf-8 -- + from __future__ import (absolute_import, division, print_function, unicode_literals) @@ -16,10 +17,17 @@ except: PyDect200 = PyDect200.PyDect200 -print(u"Welcome to PyDect200 v%s, the Python AVM-DECT200 API" % PyDect200.__version__) +fritzbox_user = getpass.getpass(prompt='Please insert your fritzbox-username: ', stream=None) fritzbox_pw = getpass.getpass(prompt='Please insert your fritzbox-password: ', stream=None) -print(u'Thank you, please wait few seconds...') -f = PyDect200(fritzbox_pw) + +print(u"Welcome to PyDect200 v%s, the Python AVM-DECT200 API" % PyDect200.__version__) + +# you can also use the .crt exported from your Browser llike FireFox +# Firfox: -> go to login site of your FritBox +# Right Click->Siteinfromation->Security->Show Certificate->Export +# Save it +f = PyDect200.PyDect200(fritzbox_pw, fritzbox_user, "/home/fritz.pem","https://fritz.box") +f.get_sid() # SID will be timedout after 1h you have to fetch an new one after timeout try: info = f.get_info() power = f.get_power_all() @@ -37,7 +45,6 @@ except: print(u"Device Name: %s" % dev_name.encode('utf-8').decode('utf-8', 'ignore')) - print(u"Device State: %s" % ('ON' if info.get(dev_id) == '1' else 'OFF')) dev_power = power.get(dev_id) if dev_power.isdigit(): @@ -45,4 +52,5 @@ print(u"Device Power: %sW" % dev_power) print(u"Device Energy: %sWh" % f.get_energy_single(dev_id)) print(u"Device Temperature: %s degree Celsius " % (f.get_temperature_single(dev_id))) - print(u'') + +f.logout() diff --git a/PyDect200/PyDect200.py b/PyDect200/PyDect200.py index e5f8504..67b5927 100644 --- a/PyDect200/PyDect200.py +++ b/PyDect200/PyDect200.py @@ -6,7 +6,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -import hashlib, sys +import hashlib, sys, ssl try: import urllib.request as urllib2 except ImportError: @@ -21,21 +21,32 @@ class PyDect200(object): __author_email__ = u'mathias@mperlet.de' __description__ = u'Control Fritz AVM DECT200' - __fritz_url = u'http://fritz.box' + __fritz_url = u'https://fritz.box' __homeswitch = u'/webservices/homeautoswitch.lua' __debug = False - def __init__(self, fritz_password): + def __init__(self, fritz_password,username, cert=None,fritz_url=None): """The constructor""" self.__password = fritz_password - self.get_sid() - + self.__username = username + self.__fritz_url = fritz_url + if not fritz_url: + self.__fritz_url = u'https://fritz.box' + + if not cert: + self.__context = ssl._create_unverified_context() + print("Warning SSL certificate unverified") + else: + self.__context = ssl.create_default_context(cafile=cert) + self.__context.verify_mode = ssl.CERT_REQUIRED + self.__context.check_hostname = True + print("SSL activ") - def set_url(self, url): + def set_url(self, url, ssl_conext): """Set alternative url""" self.__fritz_url = url - + self.__context = context def __homeauto_url_with_sid(self): """Returns formatted uri""" @@ -44,10 +55,11 @@ def __homeauto_url_with_sid(self): self.sid) @classmethod - def __query(cls, url): + def __query(cls, url,context): """Reads a URL""" + try: - return urllib2.urlopen(url).read().decode('utf-8').replace('\n', '') + return urllib2.urlopen(url,context=context).read().decode('ascii').replace('\n', '') except urllib2.HTTPError: _, exception, _ = sys.exc_info() if cls.__debug: @@ -64,22 +76,21 @@ def __query(cls, url): pass return "inval" - - def __query_cmd(self, command, device=None): """Calls a command""" url = u'%s&switchcmd=%s' % (self.__homeauto_url_with_sid(), command) if device is None: - return self.__query(url) + res = self.__query(url,self.__context) + return res else: - return self.__query('%s&ain=%s' % (url, device)) + return self.__query('%s&ain=%s' % (url, device),self.__context) def get_sid(self): """Returns a valid SID""" base_url = u'%s/login_sid.lua' % self.__fritz_url get_challenge = None try: - get_challenge = urllib2.urlopen(base_url).read().decode('ascii') + get_challenge = urllib2.urlopen(base_url,context=self.__context).read().decode('ascii') except urllib2.HTTPError as exception: print('HTTPError = ' + str(exception.code)) except urllib2.URLError as exception: @@ -88,19 +99,25 @@ def get_sid(self): print('generic exception: ' + str(exception)) raise - - challenge = get_challenge.split( - '')[1].split('')[0] - challenge_b = ( - challenge + '-' + self.__password).encode().decode('iso-8859-1').encode('utf-16le') + challenge = get_challenge.split('')[1].split('')[0] + challenge_b = (challenge + '-' + self.__password).encode().decode('iso-8859-1').encode('utf-16le') md5hash = hashlib.md5() md5hash.update(challenge_b) response_b = challenge + '-' + md5hash.hexdigest().lower() - get_sid = urllib2.urlopen('%s?response=%s' % (base_url, response_b)).read().decode('utf-8') + get_sid = urllib2.urlopen('%s?username=%s&response=%s' % (base_url, self.__username,response_b),context=self.__context).read().decode('utf-8') + self.sid = get_sid.split('')[1].split('')[0] - + + def logout(self): + url = u"%s&logout=1" % self.__homeauto_url_with_sid() + try: + urllib2.urlopen(url,context=self.__context) + except: + pass + + def get_info(self): """Returns device info""" return self.get_state_all() @@ -187,3 +204,6 @@ def get_state_all(self): for device in self.get_device_names().keys(): state_dict[device] = self.get_state(device) return state_dict + + def __del__(self): + self.logout() diff --git a/README.md b/README.md index 68d5be4..ab18bcb 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,28 @@ -PyDect200 -====== - -[![Download format](http://img.shields.io/pypi/format/PyDect200.svg)](https://pypi.python.org/pypi/PY_DECT200/) -[![Downloads](http://img.shields.io/pypi/dm/PyDect200.svg)](https://pypi.python.org/pypi/PY_DECT200/) -[![License](http://img.shields.io/pypi/l/PyDect200.svg)](https://pypi.python.org/pypi/PY_DECT200/) -[![Latest Version](http://img.shields.io/pypi/v/PyDect200.svg)](https://pypi.python.org/pypi/PY_DECT200/) - - -Control the Fritz-AVM DECT200 (switch a electric socket) -and Fritz-AVM PowerLine 546E - -### Install - -``` -pip install PyDect200 -``` - -### Demo - -``` -git clone git@github.com:mperlet/PyDect200.git - -./PyDect200/Example/PyDect200_Demo.py -``` ### Example Code ``` In [1]: from PyDect200 import PyDect200 -In [2]: f = PyDect200('fitzbox_password') -In [3]: f.get_device_names() -Out[3]: {'16': 'Beleuchtung', '17': 'Fernseher'} -In [4]: f.get_info() -Out[4]: {u'16': u'0', u'17': u'0'} -In [5]: f.switch_onoff(16,1) -Out[5]: +In [2]: f = PyDect200.PyDect200(fritzbox_pw, fritzbox_user, "/home/fritz.pem","https://fritz.box") +In [3]: f.get_sid() # get the sid will you get a new login +In [4]: f.get_device_names() +Out[4]: {'16': 'Beleuchtung', '17': 'Fernseher'} +In [5]: f.get_info() +Out[5]: {u'16': u'0', u'17': u'0'} +In [6]: f.switch_onoff(16,1) +Out[6]: {u'DeviceID': u'16', u'RequestResult': u'1', u'Value': u'0', u'ValueToSet': u'1'} -In [6]: f.get_power() -Out[6]: {u'16': 68.95, u'17': 0.0} +In [7]: f.get_power() +Out[7]: {u'16': 68.95, u'17': 0.0} +In [8]: f.logout() ``` ### Tested with -* Python2.7 / Python3.4 -* Fritzbox 7270 -* FRITZ!OS: 06.05 +* Python3.5 +* Fritzbox 7390 +* FRITZ!OS: 06.51 * AVM Dect200 - -****************** - -* Python2.7 -* Fritzbox 7490 -* FRITZ!OS: 6.36 Labor -* Dect200 -* PowerLine 546E