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
|
||||
from json import JSONDecodeError
|
||||
from os import getenv, path
|
||||
|
||||
|
||||
|
@ -16,6 +17,17 @@ class AppConfig:
|
|||
self.client_id = getenv('HMDOORIS_CLIENT_ID', 'hmdooris')
|
||||
self.client_secret = getenv('HMDOORIS_CLIENT_SECRET')
|
||||
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 == '':
|
||||
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.AppConfig import AppConfig
|
||||
from hmdooris.Template import template_or_error
|
||||
from hmdooris.ccujack import CCUJack
|
||||
|
||||
|
||||
class WebEndpoints:
|
||||
|
@ -16,10 +17,11 @@ class WebEndpoints:
|
|||
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.auth = auth
|
||||
self.basepath = basepath
|
||||
self.ccujack = ccujack
|
||||
self.config = config
|
||||
self.valid_username_re = '^[a-zA-Z0-9_-]+$'
|
||||
|
||||
|
@ -27,11 +29,8 @@ class WebEndpoints:
|
|||
app.route(path='/static/<filename>', callback=self.static)
|
||||
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='/api', callback=self.require_login(self.api_get))
|
||||
# app.get(path='/invite', callback=self.get_invite)
|
||||
# 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))
|
||||
app.get(path='/api', callback=self.api_get)
|
||||
app.post(path='/api', callback=self.require_login(self.api_post), )
|
||||
|
||||
def require_login(self, func: Callable) -> Callable:
|
||||
if self.config.requires_group is not None:
|
||||
|
@ -55,17 +54,20 @@ class WebEndpoints:
|
|||
|
||||
def api_get(self):
|
||||
"""
|
||||
Interact withe HomeMatic CCU through ccu-jack
|
||||
Interact with the HomeMatic CCU through ccu-jack
|
||||
:return:
|
||||
"""
|
||||
|
||||
return {
|
||||
'_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
|
||||
:return:
|
||||
"""
|
||||
#self.ccujack.update_lock(name)
|
||||
print(f"name {request.json}")
|
||||
return {}
|
|
@ -9,6 +9,7 @@ from bottle import Bottle, TEMPLATE_PATH
|
|||
|
||||
from hmdooris.AppConfig import AppConfig
|
||||
from hmdooris.WebEndpoints import WebEndpoints
|
||||
from hmdooris.ccujack import CCUJack
|
||||
|
||||
if __name__ == '__main__':
|
||||
argp = ArgumentParser(prog="hmdooris")
|
||||
|
@ -29,7 +30,8 @@ if __name__ == '__main__':
|
|||
"client_id": config.client_id,
|
||||
"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 = {
|
||||
'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")
|
||||
})();
|
|
@ -7,5 +7,7 @@
|
|||
<body>
|
||||
<h1>HM Dooris</h1>
|
||||
<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>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue