WIP
This commit is contained in:
parent
9565ba3596
commit
d60f8f8be0
6 changed files with 115 additions and 11 deletions
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
from json import JSONDecodeError
|
||||||
from os import getenv, path
|
from os import getenv, path
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +17,17 @@ class AppConfig:
|
||||||
self.client_id = getenv('HMDOORIS_CLIENT_ID', 'hmdooris')
|
self.client_id = getenv('HMDOORIS_CLIENT_ID', 'hmdooris')
|
||||||
self.client_secret = getenv('HMDOORIS_CLIENT_SECRET')
|
self.client_secret = getenv('HMDOORIS_CLIENT_SECRET')
|
||||||
self.requires_group = getenv('HMDOORIS_REQUIRES_GROUP', None)
|
self.requires_group = getenv('HMDOORIS_REQUIRES_GROUP', None)
|
||||||
|
self.ccujack_url = getenv('HMDOORIS_CCUJACK_URL', None)
|
||||||
|
self.ccujack_username = getenv('HMDOORIS_CCUJACK_USERNAME', None)
|
||||||
|
self.ccujack_password = getenv('HMDOORIS_CCUJACK_PASSWORD', None)
|
||||||
|
try:
|
||||||
|
self.ccujack_locks = json.loads(getenv('HMDOORIS_CCUJACK_LOCKS', '[]'))
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
raise ValueError(f"Unable to decode HMDOORIS_CCUJACK_LOCKS=\"{getenv('HMDOORIS_CCUJACK_LOCKS', '{}')}\"", e)
|
||||||
|
|
||||||
if self.client_secret is None or self.client_secret == '':
|
if self.client_secret is None or self.client_secret == '':
|
||||||
raise ValueError('You need to provide HMDOORIS_CLIENT_SECRET')
|
raise ValueError('You need to provide HMDOORIS_CLIENT_SECRET')
|
||||||
|
if self.ccujack_url is None or self.ccujack_url == '':
|
||||||
|
raise ValueError('You need to provide HMDOORIS_CCUJACK_URL')
|
||||||
|
if len(self.ccujack_locks) == 0:
|
||||||
|
raise ValueError('You need to provide HMDOORIS_CCUJACK_LOCKS as a JSON object of locks')
|
||||||
|
|
|
@ -9,6 +9,7 @@ from bottle import static_file, request, redirect, Bottle
|
||||||
from hmdooris import CSRF
|
from hmdooris import CSRF
|
||||||
from hmdooris.AppConfig import AppConfig
|
from hmdooris.AppConfig import AppConfig
|
||||||
from hmdooris.Template import template_or_error
|
from hmdooris.Template import template_or_error
|
||||||
|
from hmdooris.ccujack import CCUJack
|
||||||
|
|
||||||
|
|
||||||
class WebEndpoints:
|
class WebEndpoints:
|
||||||
|
@ -16,10 +17,11 @@ class WebEndpoints:
|
||||||
Defines endpoints for interaction with the user's browser.
|
Defines endpoints for interaction with the user's browser.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app: Bottle, auth: BottleOIDC, basepath: str, config: AppConfig):
|
def __init__(self, app: Bottle, auth: BottleOIDC, basepath: str, ccujack: CCUJack, config: AppConfig):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.auth = auth
|
self.auth = auth
|
||||||
self.basepath = basepath
|
self.basepath = basepath
|
||||||
|
self.ccujack = ccujack
|
||||||
self.config = config
|
self.config = config
|
||||||
self.valid_username_re = '^[a-zA-Z0-9_-]+$'
|
self.valid_username_re = '^[a-zA-Z0-9_-]+$'
|
||||||
|
|
||||||
|
@ -27,11 +29,8 @@ class WebEndpoints:
|
||||||
app.route(path='/static/<filename>', callback=self.static)
|
app.route(path='/static/<filename>', callback=self.static)
|
||||||
app.get(path='/', callback=template_or_error('home')(self.home))
|
app.get(path='/', callback=template_or_error('home')(self.home))
|
||||||
app.get(path='/foo', callback=self.require_login(template_or_error('home')(self.home)))
|
app.get(path='/foo', callback=self.require_login(template_or_error('home')(self.home)))
|
||||||
app.get(path='/api', callback=self.require_login(self.api_get))
|
app.get(path='/api', callback=self.api_get)
|
||||||
# app.get(path='/invite', callback=self.get_invite)
|
app.post(path='/api', callback=self.require_login(self.api_post), )
|
||||||
# app.post(path='/invite', callback=self.require_login(template_or_error('invite_result')(self.post_invite)))
|
|
||||||
# app.get(path='/claim', callback=template_or_error('claim_form')(self.get_claim))
|
|
||||||
# app.post(path='/claim', callback=template_or_error('claim_result')(self.post_claim))
|
|
||||||
|
|
||||||
def require_login(self, func: Callable) -> Callable:
|
def require_login(self, func: Callable) -> Callable:
|
||||||
if self.config.requires_group is not None:
|
if self.config.requires_group is not None:
|
||||||
|
@ -55,17 +54,20 @@ class WebEndpoints:
|
||||||
|
|
||||||
def api_get(self):
|
def api_get(self):
|
||||||
"""
|
"""
|
||||||
Interact withe HomeMatic CCU through ccu-jack
|
Interact with the HomeMatic CCU through ccu-jack
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'_csrf': CSRF.get_token(),
|
'_csrf': CSRF.get_token(),
|
||||||
'foo': 'bar'
|
'locks': self.ccujack.get_locks()
|
||||||
}
|
}
|
||||||
|
|
||||||
def api_put(self):
|
def api_post(self):
|
||||||
"""
|
"""
|
||||||
Interact withe HomeMatic CCU through ccu-jack
|
Interact withe HomeMatic CCU through ccu-jack
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
#self.ccujack.update_lock(name)
|
||||||
|
print(f"name {request.json}")
|
||||||
return {}
|
return {}
|
|
@ -9,6 +9,7 @@ from bottle import Bottle, TEMPLATE_PATH
|
||||||
|
|
||||||
from hmdooris.AppConfig import AppConfig
|
from hmdooris.AppConfig import AppConfig
|
||||||
from hmdooris.WebEndpoints import WebEndpoints
|
from hmdooris.WebEndpoints import WebEndpoints
|
||||||
|
from hmdooris.ccujack import CCUJack
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
argp = ArgumentParser(prog="hmdooris")
|
argp = ArgumentParser(prog="hmdooris")
|
||||||
|
@ -29,7 +30,8 @@ if __name__ == '__main__':
|
||||||
"client_id": config.client_id,
|
"client_id": config.client_id,
|
||||||
"client_secret": config.client_secret,
|
"client_secret": config.client_secret,
|
||||||
})
|
})
|
||||||
WebEndpoints(app, auth, config.basepath, config)
|
ccujack = CCUJack(config.ccujack_url, config.ccujack_username, config.ccujack_password, config.ccujack_locks)
|
||||||
|
WebEndpoints(app, auth, config.basepath, ccujack, config)
|
||||||
|
|
||||||
bottle_params = {
|
bottle_params = {
|
||||||
'debug': bool(getenv('BOTTLE_DEBUG', 'False')) or args.debug,
|
'debug': bool(getenv('BOTTLE_DEBUG', 'False')) or args.debug,
|
||||||
|
|
63
hmdooris/ccujack.py
Normal file
63
hmdooris/ccujack.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
|
from hmdooris.AppException import AppException
|
||||||
|
|
||||||
|
|
||||||
|
class CCUJackHmIPDLD:
|
||||||
|
def __init__(self, ccu: 'CCUJack', name: str, id:str):
|
||||||
|
self.ccu = ccu
|
||||||
|
self.name = name
|
||||||
|
self.id = id
|
||||||
|
self.value_list = self.get_value_list(id)
|
||||||
|
|
||||||
|
def status(self):
|
||||||
|
r = self.ccu.get_json(f"/device/{self.id}/1/LOCK_STATE/~pv")
|
||||||
|
return self.value_list[r["v"]]
|
||||||
|
|
||||||
|
def get_value_list(self, id) -> List[str]:
|
||||||
|
"""
|
||||||
|
Uses the fist lock to obtain the valueList property that maps the enum values to strings
|
||||||
|
:param id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
r = self.ccu.get_json(f"/device/{id}/1/LOCK_STATE")
|
||||||
|
return r["valueList"]
|
||||||
|
|
||||||
|
|
||||||
|
class CCUJack:
|
||||||
|
def __init__(self, url: str, username: str, password: str, locks:Dict):
|
||||||
|
self.url = url
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.auth = HTTPBasicAuth(username, password)
|
||||||
|
self.locks = {}
|
||||||
|
self.get_all_locks()
|
||||||
|
|
||||||
|
def get_json(self, url:str):
|
||||||
|
url = self.url + url
|
||||||
|
r = requests.get(url, auth=self.auth)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise AppException(f"Unable to talk to CCU at {url}: {r.status_code}")
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def get_all_locks(self):
|
||||||
|
r = self.get_json(f"/device")
|
||||||
|
for link in r["~links"]:
|
||||||
|
if link["rel"] == "device":
|
||||||
|
device = self.get_json(f"/device/{link['href']}")
|
||||||
|
if device["type"] == "HmIP-DLD":
|
||||||
|
self.locks[device["title"]] = CCUJackHmIPDLD(self, device["title"], device["identifier"])
|
||||||
|
|
||||||
|
|
||||||
|
def get_locks(self):
|
||||||
|
s = []
|
||||||
|
for name, lock in self.locks.items():
|
||||||
|
s.append({
|
||||||
|
"name": name,
|
||||||
|
"id": lock.id,
|
||||||
|
"status": lock.status()
|
||||||
|
})
|
||||||
|
return s
|
|
@ -1,3 +1,26 @@
|
||||||
(function(){
|
(function () {
|
||||||
|
setInterval(function () {
|
||||||
|
fetch("/api").then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
}).then(function (json) {
|
||||||
|
let e = document.getElementById("lock-status");
|
||||||
|
e.innerText = JSON.stringify(json.locks);
|
||||||
|
})
|
||||||
|
}, 10000)
|
||||||
|
|
||||||
|
for (let e of document.querySelectorAll("button.lock-unlock")) {
|
||||||
|
e.onclick = function () {
|
||||||
|
fetch("/api", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: e.name,
|
||||||
|
contentType: "application/json",
|
||||||
|
})
|
||||||
|
}).then(function (response) {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Setup complete")
|
console.log("Setup complete")
|
||||||
})();
|
})();
|
|
@ -7,5 +7,7 @@
|
||||||
<body>
|
<body>
|
||||||
<h1>HM Dooris</h1>
|
<h1>HM Dooris</h1>
|
||||||
<p>Hello, world!</p>
|
<p>Hello, world!</p>
|
||||||
|
<p>Lock Status: <span id="lock-status">unknown</span></p>
|
||||||
|
<button class="lock-unlock" name="Werkstatt">Lock/Unlock Werkstatt</button>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue