From 313b7d5bca897091df3d7859c806c75f793d0907 Mon Sep 17 00:00:00 2001 From: Kalzifer Date: Fri, 10 Jun 2016 16:29:06 +0200 Subject: [PATCH 01/10] Added SSL and user based Login - Code using now https requests Warning: the SSL is set to unverified! - PyDect200 wants now a username for your login --- PyDect200/PyDect200.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/PyDect200/PyDect200.py b/PyDect200/PyDect200.py index e5f8504..fb61eb7 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,14 +21,16 @@ 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): """The constructor""" self.__password = fritz_password + self.__username = username + self.__context = ssl._create_unverified_context() self.get_sid() @@ -47,7 +49,7 @@ def __homeauto_url_with_sid(self): def __query(cls, url): """Reads a URL""" try: - return urllib2.urlopen(url).read().decode('utf-8').replace('\n', '') + return urllib2.urlopen(url,context=ssl._create_unverified_context()).read().decode('utf-8').replace('\n', '') except urllib2.HTTPError: _, exception, _ = sys.exc_info() if cls.__debug: @@ -79,7 +81,7 @@ def get_sid(self): 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=ssl._create_unverified_context()).read().decode('ascii') except urllib2.HTTPError as exception: print('HTTPError = ' + str(exception.code)) except urllib2.URLError as exception: @@ -98,7 +100,7 @@ def get_sid(self): 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=ssl._create_unverified_context()).read().decode('utf-8') self.sid = get_sid.split('')[1].split('')[0] def get_info(self): From 75530993a2b861d0a80e4f5da16c260698dee94d Mon Sep 17 00:00:00 2001 From: Kalzifer Date: Fri, 10 Jun 2016 16:41:44 +0200 Subject: [PATCH 02/10] Added user request --- Example/PyDect200_Demo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Example/PyDect200_Demo.py b/Example/PyDect200_Demo.py index f74f064..435adf7 100755 --- a/Example/PyDect200_Demo.py +++ b/Example/PyDect200_Demo.py @@ -18,8 +18,9 @@ print(u"Welcome to PyDect200 v%s, the Python AVM-DECT200 API" % PyDect200.__version__) fritzbox_pw = getpass.getpass(prompt='Please insert your fritzbox-password: ', stream=None) +fritzbox_user = getpass.getpass(prompt='Please insert your fritzbox-username: ', stream=None) print(u'Thank you, please wait few seconds...') -f = PyDect200(fritzbox_pw) +f = PyDect200(fritzbox_pw, fritzbox_user) try: info = f.get_info() power = f.get_power_all() From b3afd73693fc0ae726323813fe0706f2bdcc0413 Mon Sep 17 00:00:00 2001 From: Frank Schmidt Date: Fri, 24 Jun 2016 10:29:05 +0200 Subject: [PATCH 03/10] Update to new Constructor username is needed --- README.md | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/README.md b/README.md index 68d5be4..e5cb6d1 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,9 @@ -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 [2]: f = PyDect200('fitzbox_password',"fritzbox_username") In [3]: f.get_device_names() Out[3]: {'16': 'Beleuchtung', '17': 'Fernseher'} In [4]: f.get_info() From 17afa1b7a8aed172b2d4bf71b01a30b39274729f Mon Sep 17 00:00:00 2001 From: Frank Schmidt Date: Tue, 30 Aug 2016 16:12:56 +0200 Subject: [PATCH 04/10] Update PyDect200.py Add logout() Add better SSL support --- PyDect200/PyDect200.py | 58 +++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/PyDect200/PyDect200.py b/PyDect200/PyDect200.py index fb61eb7..67b5927 100644 --- a/PyDect200/PyDect200.py +++ b/PyDect200/PyDect200.py @@ -26,18 +26,27 @@ class PyDect200(object): __debug = False - def __init__(self, fritz_password,username): + def __init__(self, fritz_password,username, cert=None,fritz_url=None): """The constructor""" self.__password = fritz_password self.__username = username - self.__context = ssl._create_unverified_context() - self.get_sid() - + 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""" @@ -46,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,context=ssl._create_unverified_context()).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: @@ -66,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,context=ssl._create_unverified_context()).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: @@ -90,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?username=%s&response=%s' % (base_url, self.__username,response_b),context=ssl._create_unverified_context()).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() @@ -189,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() From 868535b032fb13ae2879497055a656827d2518bf Mon Sep 17 00:00:00 2001 From: Frank Schmidt Date: Tue, 30 Aug 2016 16:20:10 +0200 Subject: [PATCH 05/10] Update README.md --- README.md | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index e5cb6d1..81c7560 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,28 @@ ### Example Code -``` +''' In [1]: from PyDect200 import PyDect200 -In [2]: f = PyDect200('fitzbox_password',"fritzbox_username") -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, "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 From f013ca77043cd3dc5a729f27d7a81d11d32a8185 Mon Sep 17 00:00:00 2001 From: Frank Schmidt Date: Tue, 30 Aug 2016 16:21:15 +0200 Subject: [PATCH 06/10] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81c7560..9f225a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ### Example Code -''' +``` In [1]: from PyDect200 import PyDect200 In [2]: f = PyDect200.PyDect200(fritzbox_pw, fritzbox_user, "fritz.pem","https://fritz.box") In [3]: f.get_sid() # get the sid will you get a new login @@ -18,7 +18,7 @@ Out[6]: In [7]: f.get_power() Out[7]: {u'16': 68.95, u'17': 0.0} In [8]: f.logout() -''' +``` ### Tested with From b840c949b2a5341d1196b9da85075f49d7112185 Mon Sep 17 00:00:00 2001 From: Frank Schmidt Date: Tue, 30 Aug 2016 16:24:22 +0200 Subject: [PATCH 07/10] Update PyDect200_Demo.py --- Example/PyDect200_Demo.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Example/PyDect200_Demo.py b/Example/PyDect200_Demo.py index 435adf7..d2eebd1 100755 --- a/Example/PyDect200_Demo.py +++ b/Example/PyDect200_Demo.py @@ -17,10 +17,8 @@ PyDect200 = PyDect200.PyDect200 print(u"Welcome to PyDect200 v%s, the Python AVM-DECT200 API" % PyDect200.__version__) -fritzbox_pw = getpass.getpass(prompt='Please insert your fritzbox-password: ', stream=None) -fritzbox_user = getpass.getpass(prompt='Please insert your fritzbox-username: ', stream=None) -print(u'Thank you, please wait few seconds...') -f = PyDect200(fritzbox_pw, fritzbox_user) +f = PyDect200.PyDect200(fritzbox_pw, fritzbox_user, "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() @@ -38,7 +36,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(): @@ -46,4 +43,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() From d728e876d208767d053e8b805340f3622324915a Mon Sep 17 00:00:00 2001 From: Frank Schmidt Date: Tue, 30 Aug 2016 16:48:54 +0200 Subject: [PATCH 08/10] Update PyDect200_Demo.py --- Example/PyDect200_Demo.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Example/PyDect200_Demo.py b/Example/PyDect200_Demo.py index d2eebd1..e16f22d 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,6 +17,9 @@ except: PyDect200 = PyDect200.PyDect200 +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"Welcome to PyDect200 v%s, the Python AVM-DECT200 API" % PyDect200.__version__) f = PyDect200.PyDect200(fritzbox_pw, fritzbox_user, "fritz.pem","https://fritz.box") f.get_sid() # SID will be timedout after 1h you have to fetch an new one after timeout From 8b94a4f004d6693fd04c391a311b5874156a76a7 Mon Sep 17 00:00:00 2001 From: Frank Schmidt Date: Tue, 30 Aug 2016 17:22:13 +0200 Subject: [PATCH 09/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f225a2..ab18bcb 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ``` In [1]: from PyDect200 import PyDect200 -In [2]: f = PyDect200.PyDect200(fritzbox_pw, fritzbox_user, "fritz.pem","https://fritz.box") +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'} From 34aded37c9b9106166c844bf4cef8108ea66cef4 Mon Sep 17 00:00:00 2001 From: Frank Schmidt Date: Tue, 30 Aug 2016 17:30:25 +0200 Subject: [PATCH 10/10] Update PyDect200_Demo.py --- Example/PyDect200_Demo.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Example/PyDect200_Demo.py b/Example/PyDect200_Demo.py index e16f22d..435aef5 100755 --- a/Example/PyDect200_Demo.py +++ b/Example/PyDect200_Demo.py @@ -21,7 +21,12 @@ fritzbox_pw = getpass.getpass(prompt='Please insert your fritzbox-password: ', stream=None) print(u"Welcome to PyDect200 v%s, the Python AVM-DECT200 API" % PyDect200.__version__) -f = PyDect200.PyDect200(fritzbox_pw, fritzbox_user, "fritz.pem","https://fritz.box") + +# 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()