Add spaceapid update endpoint
All checks were successful
docker-image / docker (push) Successful in 1m30s

Configure the device name and the update endpoint to report open status to spaceapid.
This commit is contained in:
Stefan Bethke 2025-06-13 18:17:09 +02:00
commit 7cb930be38
4 changed files with 30 additions and 2 deletions

View file

@ -18,6 +18,8 @@ All configuration is handled through environment variables.
| `IDINVITE_OIDC_SCOPE` | `["openid", "email", "profile"]` | JSON list of OIDC scopes to request. The OIDC IDP will need to send the group attribute. | | `IDINVITE_OIDC_SCOPE` | `["openid", "email", "profile"]` | JSON list of OIDC scopes to request. The OIDC IDP will need to send the group attribute. |
| `IDINVITE_OIDC_USER_ATTR` | `email` | The attribute to use as the user ID. | | `IDINVITE_OIDC_USER_ATTR` | `email` | The attribute to use as the user ID. |
| `HMDOORIS_REQUIRES_GROUP` | - | Set to require users to be a member of this groups. | | `HMDOORIS_REQUIRES_GROUP` | - | Set to require users to be a member of this groups. |
| `HMDOORIS_SPACEAPI_OPEND_NAME` | - | Name of the device to send `spaceapid` updates for |
| `HMDOORIS_SPACEAPI_OPEN_URL` | - | URL for the `spaceapid` open update endpoint. |
| `HMDOORIS_URL` | `http://localhost:3000` | URL of the application, used to construct links to itself. | | `HMDOORIS_URL` | `http://localhost:3000` | URL of the application, used to construct links to itself. |
### Required Group ### Required Group
@ -34,6 +36,14 @@ RaspberryMatic. If you are using a private certificate, you will need to use `HM
the HTTP client to a suitable CA certificate. Setting the variable to `false` will disable certificate verification. the HTTP client to a suitable CA certificate. Setting the variable to `false` will disable certificate verification.
Alternatively, you can use plain `http`. Alternatively, you can use plain `http`.
### SpaceAPI
[spaceapid](https://git.hamburg.ccc.de/CCCHH/spaceapid) can be used to provide data
for [SpaceAPI](https://spaceapi.io), and it's open update endpoint can be used to report the open status of a space. By
setting `HMDOORIS_SPACEAPI_OPEND_NAME` to the name of the lock on the main door, and `HMDOORIS_SPACEAPI_OPEN_URL` to the
update endpoint URL hmdooris can update the open status whenever it changes. Add the username and password in the url,
in the `https://user:pass@hostname.tld` format. If you do not set the name, no updates will be sent.
## Managing the CCU certificate ## Managing the CCU certificate
If you want to talk to the RaspberryMatic/CCU-Jack and you are using a self-signed certificate (which is the default), If you want to talk to the RaspberryMatic/CCU-Jack and you are using a self-signed certificate (which is the default),

View file

@ -28,6 +28,8 @@ class AppConfig:
self.ccujack_certificate_path = getenv('HMDOORIS_CCUJACK_CERTIFICATE_PATH', None) self.ccujack_certificate_path = getenv('HMDOORIS_CCUJACK_CERTIFICATE_PATH', None)
self.ccujack_username = getenv('HMDOORIS_CCUJACK_USERNAME', None) self.ccujack_username = getenv('HMDOORIS_CCUJACK_USERNAME', None)
self.ccujack_password = getenv('HMDOORIS_CCUJACK_PASSWORD', None) self.ccujack_password = getenv('HMDOORIS_CCUJACK_PASSWORD', None)
self.spaceapid_open_name = getenv('HMDOORIS_SPACEAPI_OPEND_NAME', None)
self.spaceapid_open_url = getenv('HMDOORIS_SPACEAPI_OPEN_URL', 'https://spaceapi.hamburg.ccc.de/state/open')
if self.debug is not None and self.debug.lower not in ('0', 'f', 'false'): if self.debug is not None and self.debug.lower not in ('0', 'f', 'false'):
self.debug = True self.debug = True

View file

@ -39,7 +39,10 @@ auth = BottleOIDC(app, config={
websocket_clients = WebSocketClients() websocket_clients = WebSocketClients()
bottle_helpers = BottleHelpers(auth, group=config.requires_group, allowed=config.allowed) bottle_helpers = BottleHelpers(auth, group=config.requires_group, allowed=config.allowed)
update_poller = UpdatePoller(websocket_clients, ccujack, 1 if config.debug else 0.1) update_poller = UpdatePoller(websocket_clients, ccujack,
update_delay=1 if config.debug else 0.1,
spaceapid_open_name=config.spaceapid_open_name,
spaceapid_open_url=config.spaceapid_open_url)
@app.route("/static/<filepath>") @app.route("/static/<filepath>")

View file

@ -3,16 +3,23 @@ import logging
from threading import Thread from threading import Thread
from time import sleep from time import sleep
import requests
from hmdooris.ccujack import CCUJack from hmdooris.ccujack import CCUJack
from hmdooris.websocketcomm import WebSocketClients from hmdooris.websocketcomm import WebSocketClients
class UpdatePoller: class UpdatePoller:
def __init__(self, wsc: WebSocketClients, ccu: CCUJack, update_delay = 1.0): def __init__(self, wsc: WebSocketClients, ccu: CCUJack,
update_delay = 1.0,
spaceapid_open_name=None,
spaceapid_open_url=None):
self.wsc = wsc self.wsc = wsc
self.ccu = ccu self.ccu = ccu
self.current = {} self.current = {}
self.update_delay = update_delay self.update_delay = update_delay
self.spaceapid_open_name = spaceapid_open_name
self.spaceapid_open_url = spaceapid_open_url
self.log = logging.getLogger(__name__) self.log = logging.getLogger(__name__)
Thread(target=self.run, daemon=True).start() Thread(target=self.run, daemon=True).start()
@ -33,6 +40,7 @@ class UpdatePoller:
for lock_id, value in new.items(): for lock_id, value in new.items():
if force or lock_id not in self.current or self.current[lock_id] != value: if force or lock_id not in self.current or self.current[lock_id] != value:
update.append(value) update.append(value)
self.spaceapid_open_update(value)
data = { data = {
"locks": update, "locks": update,
} }
@ -42,3 +50,8 @@ class UpdatePoller:
def send_locks(self, force=False): def send_locks(self, force=False):
self.wsc.send(self.get_locks()) self.wsc.send(self.get_locks())
def spaceapid_open_update(self, lock):
if self.spaceapid_open_name is None:
return
requests.put(self.spaceapid_open_url, data=lock["status"]=="UNLOCKED")