mirror of
https://github.com/binary-kitchen/doorlockd
synced 2024-12-22 18:34:25 +01:00
Add nuki bridge backend
Add a backend to support nuki bridges Signed-off-by: Thomas Schmid <tom@lfence.de>
This commit is contained in:
parent
4825c7346e
commit
94b95c04a7
@ -24,6 +24,7 @@ from threading import Thread
|
|||||||
from time import sleep
|
from time import sleep
|
||||||
from os.path import join
|
from os.path import join
|
||||||
from pydoorlock.AvrDoorlock import AvrDoorlockBackend
|
from pydoorlock.AvrDoorlock import AvrDoorlockBackend
|
||||||
|
from pydoorlock.NukiBridge import NukiBridge
|
||||||
from pydoorlock.SimulationBackend import SimulationBackend
|
from pydoorlock.SimulationBackend import SimulationBackend
|
||||||
|
|
||||||
from .Config import Config
|
from .Config import Config
|
||||||
@ -134,6 +135,10 @@ class DoorHandler:
|
|||||||
|
|
||||||
if backend_type == "avr":
|
if backend_type == "avr":
|
||||||
self.backend = AvrDoorlockBackend(self)
|
self.backend = AvrDoorlockBackend(self)
|
||||||
|
elif backend_type == "nuki":
|
||||||
|
self.backend = NukiBridge(cfg.str("NUKI_ENDPOINT", "backend"),
|
||||||
|
cfg.str("NUKI_APITOKEN", "backend"),
|
||||||
|
cfg.str("NUKI_DEVICE", "backend"))
|
||||||
elif backend_type == "simulation":
|
elif backend_type == "simulation":
|
||||||
self.backend = SimulationBackend(self)
|
self.backend = SimulationBackend(self)
|
||||||
else:
|
else:
|
||||||
|
127
pydoorlock/NukiBridge.py
Normal file
127
pydoorlock/NukiBridge.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
from .Door import DoorState
|
||||||
|
from .DoorlockBackend import DoorlockBackend
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class NukiBridgeDevice():
|
||||||
|
"""
|
||||||
|
class to interface with a nuki smartlock
|
||||||
|
"""
|
||||||
|
def __init__(self, endpoint: str, apitoken: str, device_name: str) -> None:
|
||||||
|
self.endpoint = endpoint
|
||||||
|
self.apitoken = apitoken
|
||||||
|
|
||||||
|
devices = self.list()
|
||||||
|
|
||||||
|
if devices is None:
|
||||||
|
log.error("Unable to get device id")
|
||||||
|
raise RuntimeError("unable to get nuki device id")
|
||||||
|
for device in devices:
|
||||||
|
if device["name"] == device_name:
|
||||||
|
self.device_id = device["nukiId"]
|
||||||
|
|
||||||
|
def get_device_id(self):
|
||||||
|
return self.device_id
|
||||||
|
|
||||||
|
def make_request(self, endpoint, parameters: dict = {}):
|
||||||
|
url = urljoin(self.endpoint, endpoint)
|
||||||
|
parameters["token"] = self.apitoken
|
||||||
|
|
||||||
|
try:
|
||||||
|
return requests.get(url, parameters)
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"{e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def info(self):
|
||||||
|
response = self.make_request("info")
|
||||||
|
return response
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
response = self.make_request("list")
|
||||||
|
|
||||||
|
if response is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return json.loads(response.content)
|
||||||
|
|
||||||
|
def get_device_state(self):
|
||||||
|
response = self.list()
|
||||||
|
|
||||||
|
if response is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for info in response:
|
||||||
|
if info["nukiId"] == self.get_device_id():
|
||||||
|
return info["lastKnownState"]
|
||||||
|
|
||||||
|
def lock(self, device_name: str = None):
|
||||||
|
if device_name == None:
|
||||||
|
nukiId = self.device_id
|
||||||
|
else:
|
||||||
|
RuntimeError("")
|
||||||
|
|
||||||
|
response = self.make_request("lock", {"nukiid": nukiId})
|
||||||
|
|
||||||
|
if response is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_response = json.loads(response.content)
|
||||||
|
return json_response["success"]
|
||||||
|
|
||||||
|
log.error(f"Nuki responded with an error: {json_response}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def unlock(self, device_name: str = None):
|
||||||
|
if device_name == None:
|
||||||
|
nukiId = self.device_id
|
||||||
|
else:
|
||||||
|
RuntimeError("")
|
||||||
|
|
||||||
|
response = self.make_request("unlock", {"nukiid": nukiId})
|
||||||
|
|
||||||
|
if response is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_response = json.loads(response.content)
|
||||||
|
return json_response["success"]
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class NukiBridge(DoorlockBackend):
|
||||||
|
def __init__(self, endpoint, api_key, device_name):
|
||||||
|
#self.device = NukiBridgeDevice(endpoint,api_key, device_name)
|
||||||
|
self.device = NukiBridgeDevice(endpoint, api_key, device_name)
|
||||||
|
self.poll_thread = threading.Thread(target=self.poll_worker)
|
||||||
|
self.poll_thread.start()
|
||||||
|
|
||||||
|
def poll_worker(self):
|
||||||
|
while True:
|
||||||
|
state = self.device.get_device_state()
|
||||||
|
|
||||||
|
if state is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.current_state = state
|
||||||
|
print(state)
|
||||||
|
time.sleep(10)
|
||||||
|
|
||||||
|
def set_state(self, state):
|
||||||
|
if state == DoorState.Open:
|
||||||
|
log.info("open nuki")
|
||||||
|
return self.device.unlock()
|
||||||
|
elif state == DoorState.Closed:
|
||||||
|
log.info("close nuki")
|
||||||
|
return self.device.lock()
|
||||||
|
|
||||||
|
def get_state(self, state):
|
||||||
|
return self.current_state
|
@ -202,4 +202,4 @@ def webapp_run(cfg, my_logic, status, version, template_folder, static_folder):
|
|||||||
webapp.template_folder = template_folder
|
webapp.template_folder = template_folder
|
||||||
webapp.static_folder = static_folder
|
webapp.static_folder = static_folder
|
||||||
webapp.debug = debug
|
webapp.debug = debug
|
||||||
webapp.run(host=host, port=8080)
|
webapp.run(host = host, port = 8080, use_reloader=False)
|
||||||
|
Loading…
Reference in New Issue
Block a user