Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin Swelin committed Oct 31, 2016
0 parents commit dda86c4
Show file tree
Hide file tree
Showing 15 changed files with 551 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
login.py
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2016 Colin Swelin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1 change: 1 addition & 0 deletions instructions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://github.com/cswelin/kevo-polyglot
1 change: 1 addition & 0 deletions kevoPy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .kevopy import KevoPy
173 changes: 173 additions & 0 deletions kevoPy/kevopy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
import requests
import re
import json
import time

from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

class KevoPy(object):

_username = ''
_password = ''
_host = ''

_loginPath = 'login'
_signinPath = 'signin'

_locks = {}

def __init__(self, username, password, host='https://www.mykevo.com/'):

self._username = username
self._password = password

self._host = host
self._session = requests.Session()

def _reteiveToken(self):

response = self._session.get(self._host + self._loginPath, verify=False)
# response = self._session.get(self._host + self._loginPath, verify=False)
# print response
responseText = response.text

if response.url.endswith("user/locks"):
return

# print response.url
p = re.compile(r'input name="authenticity_token".*?value=\"(.*?)\"', re.MULTILINE | re.DOTALL)
values = re.findall(p, responseText)

self._token = values[0].strip()

def _praseLockIdentifiers(self,html):

p = re.compile(r'<div class=\'lock_unlock_container\' data-bolt-state=\'.*?\' data-lock-id=\'(.*?)\' id', re.MULTILINE | re.DOTALL)
# print html
values = re.findall(p, html)

# print html
for value in values:
kevo = KevoLock(value.strip(), self._session, self._host)
kevo.refresh()
self._locks[kevo.lock_id] = kevo

def connect(self):

self._reteiveToken()

r = self._session.post(self._host + self._signinPath,

data={"user[username]" : self._username,
"user[password]" : self._password,
"authenticity_token" : self._token,
"commit" : "Sign In",
"utf8" : "✓"})

self._praseLockIdentifiers(r.text)

def locks(self):
return self._locks.values()

def refreshAll(self):
for lock in self.locks():
lock.refresh()


class KevoLock(object):

_lock_data = {}
_unlock = 'user/remote_locks/command/remote_unlock.json?arguments='
_lock = 'user/remote_locks/command/remote_lock.json?arguments='
_info = 'user/remote_locks/command/lock.json?arguments='
_session = ''
_host = ''

def __init__(self, lock_id, session, host):

self.lock_id = lock_id
self._session = session
self._host = host

def refresh(self):

r = self._session.get(self._host + self._info + self.lock_id)
self._lock_data = json.loads(r.text)

def name(self):

return self._lock_data["name"]

def identifier(self):

return self._lock_data["id"]

def query(self):

return self._lock_data["bolt_state"]

def unlock(self):

try:

self._expected_state = "Unlocked"
self._previous_lock_state = self._lock_data["bolt_state"]

r = self._session.get(self._host + self._unlock + self.lock_id)
return self.verifyConfirmation()

except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError, TypeError) as e:

return False

def lock(self):

try:

self._expected_state = "Locked"
self._previous_lock_state = self._lock_data["bolt_state"]

r = self._session.get(self._host + self._lock + self.lock_id)
return self.verifyConfirmation()

except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError, TypeError) as e:

return False

def toggle(self):

if self.query() == "Unlocked":
self.lockLock()
else:
self.unlockLock()


def verifyConfirmation(self):

timeout = time.time() + 60*2
while self._expected_state != self._lock_data["bolt_state"]:

if time.time() > timeout:
return False

time.sleep(15)

self.refresh()

self.parent.update_driver()
return True


def main():

kevo = KevoPy('user', 'pass')
kevo.connect()

for lock in kevo.locks():
print lock.query()


if __name__ == '__main__':
main()
58 changes: 58 additions & 0 deletions polykevo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from polyglot.nodeserver_api import SimpleNodeServer, PolyglotConnector
from polykevo_types import KevoDiscovery, KevoLock

VERSION = "0.1.0"

class KevoNodeServer(SimpleNodeServer):

controller = []
locks = []

def setup(self):
super(KevoNodeServer, self).setup()
manifest = self.config.get('manifest',{})

self.controller = KevoDiscovery(self,'disco','Kevo Discovery', True, manifest)
# self.poly.logger.info("FROM Poly ISYVER: " + self.poly.isyver)
self.controller.discover()
self.update_config()

# self.nodes['kevocontrol'].discover()

def poll(self):
self.poly.logger.info("poll")
if len(self.locks) >= 1:
self.controller.refreshAll()


def long_poll(self):
self.poly.logger.info("long_poll")
if len(self.locks) >= 1:
self.controller.refreshAll()


def report_drivers(self):
self.poly.logger.info("report_drivers")
# if len(self.controller.locks()) >= 1:
# for i in self.locks:
# i.report_driver()


def main():

poly = PolyglotConnector()

nserver = KevoNodeServer(poly, 20, 40)
poly.connect()
poly.wait_for_config()

poly.logger.info("Kevo Interface version " + VERSION + "created")
nserver.setup()

poly.logger.info("Setup completed. Running Server.")

nserver.run()


if __name__ == "__main__":
main()
122 changes: 122 additions & 0 deletions polykevo_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from polyglot.nodeserver_api import Node
from kevoPy import KevoPy
from login import USERNAME, PASSWORD
import hashlib

class KevoDiscovery(Node):

def __init__(self, *args, **kwargs):
super(KevoDiscovery, self).__init__(*args, **kwargs)
self.kevo = KevoPy(USERNAME, PASSWORD)

def id_2_addr(self, udn):
''' convert udn id to isy address '''
hasher = hashlib.md5()
hasher.update(udn)
return hasher.hexdigest()[0:14]

def discover(self, **kwargs):

manifest = self.parent.config.get('manifest', {})
self.parent.poly.logger.info("Discovering Kevo Locks...")
self.parent.poly.logger.info("User: %s", USERNAME)
self.kevo.connect()

self.parent.poly.logger.info("Found %d Locks", len(self.kevo.locks()))

if len(self.kevo.locks()) > 0:
for lock in self.kevo.locks():

name = lock.name()
address = self.id_2_addr(lock.identifier())

lnode = self.parent.get_node(address)
if not lnode:
self.logger.info('Adding new Kevo Lock: %s(%s)', name, lock.lock_id)
lock = KevoLock(self.parent, self.parent.get_node('disco'), address, lock, manifest)

self.parent.locks.append(lock)
else:
self.logger.info('Kevo Lock: %s(%s) already added', name, lock.lock_id)
else:
self.logger.info("No Locks found")

return True

def query(self, **kwargs):
self.parent.report_drivers()
return True

def refreshAll(self):
self.kevo.refreshAll()

_drivers = {}

_commands = {'NETDISCO': discover}

node_def_id = 'KEVODISCO'

class KevoLock(Node):

def __init__(self, parent, primary, address, lock, manifest=None):

lock.parent = self
self.logger = parent.poly.logger
self.parent = parent
self.name = lock.name()
self.lock = lock
self.logger.info('Address: %s', address)
# super(KevoLock, self).__init__(parent, address, "Kevo " + lock.name(), primary, manifest)
super(KevoLock, self).__init__(parent, address, "Kevo " + lock.name(), primary, manifest)
self.logger.info('Initializing New Kevo Lock')
self.update_info()

def _setOn(self, **kwargs):

result = self.lock.lock()
if result == True:
self.report_driver()

return result

def _setOff(self, **kwargs):

result = self.lock.unlock()
if result == True:
self.report_driver()

return result


def _stateToUOM(self, state):
return {'Unlocked' : 0,
'Locked' : 100,
'Unknown' : 101,
'Bolt Jam' : 102,
'Processing' : 303,
'Confirming' : 304}[state]

def query(self, **kwargs):
self.update_info()
self.report_driver()
return True

def update_info(self):
self.lock.refresh()
self.update_driver()

def update_driver(self):
try:
self.logger.info("Setting ST to %d", self._stateToUOM(self.lock.query()))
self.set_driver('ST', self._stateToUOM(self.lock.query()))
except requests.exceptions.ConnectionError as e:
self.logger.error('Connection error to ISY: %s', e)


_drivers = {'ST': [0, 11, int]}

_commands = {'DON': _setOn,
'DOF': _setOff,
'ST' : query}

node_def_id = 'KEVO'
Loading

0 comments on commit dda86c4

Please sign in to comment.