Skip to content

Commit

Permalink
Merge pull request #33 from Danielhiversen/patch-2
Browse files Browse the repository at this point in the history
digestmod=hashlib.md5 is requried for python3.8
  • Loading branch information
LinuxChristian authored Apr 7, 2020
2 parents 63e50b8 + 42ba485 commit ec71cc0
Showing 1 changed file with 26 additions and 26 deletions.
52 changes: 26 additions & 26 deletions pyW215/pyW215.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
from urllib2 import Request, urlopen
from urllib2 import URLError, HTTPError
import xml.etree.ElementTree as ET
import hashlib
import hmac
import time
import logging

_LOGGER = logging.getLogger(__name__)


ON = 'ON'
OFF = 'OFF'


class SmartPlug(object):
"""
Class to access:
Expand All @@ -38,8 +39,8 @@ class SmartPlug(object):
Class layout is inspired by @rkabadi (https://github.com/rkabadi) for the Edimax Smart plug.
"""

def __init__(self, ip, password, user = "admin",
use_legacy_protocol = False):
def __init__(self, ip, password, user="admin",
use_legacy_protocol=False):
"""
Create a new SmartPlug instance identified by the given URL and password.
Expand All @@ -58,7 +59,7 @@ def __init__(self, ip, password, user = "admin",
if self.use_legacy_protocol:
_LOGGER.info("Enabled support for legacy firmware.")
self._error_report = False
self.model_name = self.SOAPAction(Action="GetDeviceSettings", responseElement="ModelName", params = "")
self.model_name = self.SOAPAction(Action="GetDeviceSettings", responseElement="ModelName", params="")

def moduleParameters(self, module):
"""Returns moduleID XML.
Expand All @@ -78,7 +79,7 @@ def controlParameters(self, module, status):
:param status: The state to set (i.e. true (on) or false (off))
:return XML string to join with payload
"""
if self.use_legacy_protocol :
if self.use_legacy_protocol:
return '''{}<NickName>Socket 1</NickName><Description>Socket 1</Description>
<OPStatus>{}</OPStatus><Controller>1</Controller>'''.format(self.moduleParameters(module), status)
else:
Expand All @@ -93,7 +94,6 @@ def radioParameters(self, radio):
"""
return '''<RadioID>{}</RadioID>'''.format(radio)


def requestBody(self, Action, params):
"""Returns the request payload for an action as XML>.
Expand All @@ -112,9 +112,8 @@ def requestBody(self, Action, params):
</soap:Body>
</soap:Envelope>
'''.format(Action, params, Action)


def SOAPAction(self, Action, responseElement, params = "", recursive = False):
def SOAPAction(self, Action, responseElement, params="", recursive=False):
"""Generate the SOAP action call.
:type Action: str
Expand All @@ -131,7 +130,7 @@ def SOAPAction(self, Action, responseElement, params = "", recursive = False):
if self.authenticated is None:
self.authenticated = self.auth()
auth = self.authenticated
#If not legacy protocol, ensure auth() is called for every call
# If not legacy protocol, ensure auth() is called for every call
if not self.use_legacy_protocol:
self.authenticated = None

Expand All @@ -140,15 +139,15 @@ def SOAPAction(self, Action, responseElement, params = "", recursive = False):
payload = self.requestBody(Action, params)

# Timestamp in microseconds
time_stamp = str(round(time.time()/1e6))
time_stamp = str(round(time.time() / 1e6))

action_url = '"http://purenetworks.com/HNAP1/{}"'.format(Action)
AUTHKey = hmac.new(auth[0].encode(), (time_stamp+action_url).encode()).hexdigest().upper() + " " + time_stamp
AUTHKey = hmac.new(auth[0].encode(), (time_stamp + action_url).encode(), digestmod=hashlib.md5).hexdigest().upper() + " " + time_stamp

headers = {'Content-Type' : '"text/xml; charset=utf-8"',
headers = {'Content-Type': '"text/xml; charset=utf-8"',
'SOAPAction': '"http://purenetworks.com/HNAP1/{}"'.format(Action),
'HNAP_AUTH' : '{}'.format(AUTHKey),
'Cookie' : 'uid={}'.format(auth[1])}
'HNAP_AUTH': '{}'.format(AUTHKey),
'Cookie': 'uid={}'.format(auth[1])}

try:
response = urlopen(Request(self.url, payload.encode(), headers))
Expand Down Expand Up @@ -264,13 +263,13 @@ def temperature(self):
return res

def get_temperature(self):
"""Get the device temperature in celsius."""
"""Get the device temperature in celsius."""
return self.temperature

@property
def state(self):
"""Get the device state (i.e. ON or OFF)."""
response = self.SOAPAction('GetSocketSettings', 'OPStatus', self.moduleParameters("1"))
response = self.SOAPAction('GetSocketSettings', 'OPStatus', self.moduleParameters("1"))
if response is None:
return 'unknown'
elif response.lower() == 'true':
Expand Down Expand Up @@ -317,8 +316,8 @@ def auth(self):
payload = self.initial_auth_payload()

# Build initial header
headers = {'Content-Type' : '"text/xml; charset=utf-8"',
'SOAPAction': '"http://purenetworks.com/HNAP1/Login"'}
headers = {'Content-Type': '"text/xml; charset=utf-8"',
'SOAPAction': '"http://purenetworks.com/HNAP1/Login"'}

# Request privatekey, cookie and challenge
try:
Expand All @@ -336,7 +335,8 @@ def auth(self):
CookieResponse = root.find('.//{http://purenetworks.com/HNAP1/}Cookie')
PublickeyResponse = root.find('.//{http://purenetworks.com/HNAP1/}PublicKey')

if (ChallengeResponse == None or CookieResponse == None or PublickeyResponse == None) and self._error_report is False:
if (
ChallengeResponse == None or CookieResponse == None or PublickeyResponse == None) and self._error_report is False:
_LOGGER.warning("Failed to receive initial authentication from smartplug.")
self._error_report = True
return None
Expand All @@ -349,15 +349,15 @@ def auth(self):
Publickey = PublickeyResponse.text

# Generate hash responses
PrivateKey = hmac.new((Publickey+self.password).encode(), (Challenge).encode()).hexdigest().upper()
login_pwd = hmac.new(PrivateKey.encode(), Challenge.encode()).hexdigest().upper()
PrivateKey = hmac.new((Publickey + self.password).encode(), (Challenge).encode(), digestmod=hashlib.md5).hexdigest().upper()
login_pwd = hmac.new(PrivateKey.encode(), Challenge.encode(), digestmod=hashlib.md5).hexdigest().upper()

response_payload = self.auth_payload(login_pwd)
# Build response to initial request
headers = {'Content-Type' : '"text/xml; charset=utf-8"',
'SOAPAction': '"http://purenetworks.com/HNAP1/Login"',
'HNAP_AUTH' : '"{}"'.format(PrivateKey),
'Cookie' : 'uid={}'.format(Cookie)}
headers = {'Content-Type': '"text/xml; charset=utf-8"',
'SOAPAction': '"http://purenetworks.com/HNAP1/Login"',
'HNAP_AUTH': '"{}"'.format(PrivateKey),
'Cookie': 'uid={}'.format(Cookie)}
response = urlopen(Request(self.url, response_payload, headers))
xmlData = response.read().decode()
root = ET.fromstring(xmlData)
Expand All @@ -370,7 +370,7 @@ def auth(self):
self._error_report = True
return None

self._error_report = False # Reset error logging
self._error_report = False # Reset error logging
return (PrivateKey, Cookie)

def initial_auth_payload(self):
Expand Down

0 comments on commit ec71cc0

Please sign in to comment.