Add connection and battery status
All checks were successful
docker-image / docker (push) Successful in 1m17s

This commit is contained in:
Stefan Bethke 2025-05-30 14:19:17 +02:00
commit c05e5786de
6 changed files with 101 additions and 165 deletions

View file

@ -1,6 +1,5 @@
import json
import logging
import ssl
from typing import List, Dict
import requests
@ -17,11 +16,22 @@ class CCUJackHmIPDLD:
self.name = name
self.id = id
self.value_list = self.get_value_list(id)
self.ops = {
'low_battery': True,
'unreachable': True,
'voltage': -99,
}
def status(self):
r = self.ccu.get_json(f"/device/{self.id}/1/LOCK_STATE/~pv")
return self.value_list[r["v"]]
def get_ops(self):
self.ops['low_battery'] = self.ccu.get_value(f"/device/{self.id}/0/LOW_BAT/~pv")
self.ops['unreachable'] = self.ccu.get_value(f"/device/{self.id}/0/UNREACH/~pv")
self.ops['voltage'] = self.ccu.get_value(f"/device/{self.id}/0/OPERATING_VOLTAGE/~pv")
return self.ops
def get_value_list(self, id) -> List[str]:
"""
Uses the fist lock to obtain the valueList property that maps the enum values to strings
@ -47,7 +57,7 @@ class CCUJack:
self.kvargs['verify'] = certpath
if certpath is True:
urllib3.disable_warnings()
self.locks : Dict[str, CCUJackHmIPDLD] = {}
self.locks: Dict[str, CCUJackHmIPDLD] = {}
self.get_all_locks()
def get_json(self, url: str):
@ -61,6 +71,9 @@ class CCUJack:
pass
return {}
def get_value(self, url: str):
return self.get_json(url)["v"]
def put_json(self, url: str, data: dict):
url = self.url + url
r = requests.put(url, json=data, **self.kvargs)
@ -86,11 +99,12 @@ class CCUJack:
s[lock.id] = {
"name": lock.name,
"id": lock.id,
"status": lock.status()
"status": lock.status(),
'ops': lock.get_ops(),
}
return s
def lock_unlock(self, lock_id, locked_state = None):
def lock_unlock(self, lock_id, locked_state=None):
lock = self.locks.get(lock_id)
s = lock.status()
if locked_state is None:

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Before After
Before After

View file

@ -6,6 +6,12 @@ body {
font-family: Arial, Helvetica, sans-serif;
}
@keyframes blink-animation {
to {
visibility: hidden;
}
}
#locks {
width: 40em;
}
@ -30,11 +36,16 @@ body {
margin: 1em 0 0 0;
}
.lock__line .lock__state-bat {
color: red;
animation: blink-animation 1s steps(5, start) infinite;
}
.lock__line button {
min-width: 10em;
margin: 0.1em 0.5em;
padding: 0.1em 0.5em;
border-radius: 1em;
border-radius: 2em;
background-color: white;
color: black;
}
@ -65,3 +76,17 @@ svg.icon {
position: absolute;
bottom: 0;
}
.legend {
width: 40em;
}
.legend__icons {
list-style-type: none;
padding: calc(32px + 0.3em);
text-indent: calc(-32px - 0.3em);
}
.legend__icons .icon {
color: black;
}

View file

@ -1,16 +1,17 @@
(function () {
let update_button = function (lock) {
let state_icon = document.getElementById(`lock__state-icon__${lock.id}`);
let state_label = document.getElementById(`lock__state-label__${lock.id}`);
let button_lock = document.getElementById(`lock__lock__${lock.id}`);
let button_unlock = document.getElementById(`lock__unlock__${lock.id}`);
let state_icon = document.querySelector(`#lock__line__${lock.id} .lock__state-icon`);
let state_label = document.querySelector(`#lock__line__${lock.id} .lock__state-label`);
let state_bat = document.querySelector(`#lock__line__${lock.id} .lock__state-bat`);
let button_lock = document.querySelector(`#lock__line__${lock.id} .lock__button__lock`);
let button_unlock = document.querySelector(`#lock__line__${lock.id} .lock__button__unlock`);
if (lock.status === "LOCKED") {
state_icon.innerHTML = '<svg class="icon"><use href="static/icon-lock-alt-svgrepo-com"/></svg>';
state_icon.innerHTML = '<svg class="icon"><use href="static/icons.svg#icon-lock-alt-svgrepo-com"/></svg>';
state_label.innerText = 'locked';
button_lock.disabled = true;
button_unlock.disabled = false;
} else if (lock.status === "UNLOCKED") {
state_icon.innerHTML = '<svg class="icon"><use href="static/icon-unlock-alt-svgrepo-com"/></svg>';
state_icon.innerHTML = '<svg class="icon"><use href="static/icons.svg#icon-unlock-alt-svgrepo-com"/></svg>';
state_label.innerText = 'unlocked';
button_lock.disabled = false;
button_unlock.disabled = true;
@ -20,6 +21,13 @@
button_lock.disabled = true;
button_unlock.disabled = true;
}
state_bat.innerHTML = '';
if (lock.ops.low_battery) {
state_bat.innerHTML = state_bat.innerHTML + '<svg class="icon"><use href="static/icons.svg#icon-mobile-bolt-svgrepo-com"/></svg>';
}
if (lock.ops.low_battery) {
state_bat.innerHTML = state_bat.innerHTML + '<svg class="icon"><use href="static/icons.svg#icon-broken-link-svgrepo-com"/></svg>';
}
}
let lock_button_add_event_handler = function (e, id, locking) {
@ -68,20 +76,24 @@
let lock_line = document.createElement("div");
lock_line.id = `lock__line__${lock.id}`;
lock_line.classList.add("lock__line");
lock_line.innerHTML = `<div class="lock__state-and-label"><span id="lock__state-icon__${lock.id}" class="lock__state-icon"></span><span class="lock__name">${lock.name}:&nbsp;</span><span id="lock__state-label__${lock.id}" class="lock__state-label"></span></div>`;
let lock_line_inner = `<span class="lock__state-icon"></span>`
lock_line_inner += `<span class="lock__name">${lock.name}:&nbsp;</span>`
lock_line_inner += `<span class="lock__state-label"></span>`
lock_line_inner += `<span class="lock__state-bat"></span>`
lock_line.innerHTML = `<div class="lock__state-and-label">${lock_line_inner}</div>`;
locks.appendChild(lock_line);
let lock_button = document.createElement("button");
lock_button.classList.add("lock__button__lock");
lock_button.name = lock.id;
lock_button.id = `lock__lock__${lock.id}`;
lock_button.disabled = true;
lock_button.innerText = "Lock";
lock_button_add_event_handler(lock_button, lock.id, true);
lock_line.appendChild(lock_button);
lock_button = document.createElement("button");
lock_button.classList.add("lock__button__unlock");
lock_button.name = lock.id;
lock_button.id = `lock__unlock__${lock.id}`;
lock_button.disabled = true;
lock_button.innerText = "Unlock";
lock_button_add_event_handler(lock_button, lock.id, false);

View file

@ -1,147 +0,0 @@
/* Generated using nucleoapp.com */
/* --------------------------------
General
-------------------------------- */
:root {
--icon-color-primary: inherit;
--icon-color-secondary: currentColor;
}
.icon {
display: inline-block;
color: var(--icon-color-primary); /* icon primary color */
height: 1em;
width: 1em;
line-height: 1;
flex-shrink: 0;
max-width: initial;
}
.icon use {
/* icon secondary color */
fill: var(--icon-color-secondary);
stroke: var(--icon-color-secondary);
}
/* --------------------------------
Themes
-------------------------------- */
.icon-theme-1 {
--icon-color-primary: #212121;
--icon-color-secondary: inherit;
}
/* --------------------------------
Sizes
-------------------------------- */
:root {
--icon-sm: 0.8em;
--icon-lg: 1.2em;
}
/* relative units */
.icon-sm {
font-size: var(--icon-sm);
}
.icon-lg {
font-size: var(--icon-lg);
}
/* absolute units */
.icon-16 {
font-size: 16px;
}
.icon-32 {
font-size: 32px;
}
/* --------------------------------
Stroke
-------------------------------- */
.stroke-1 {
stroke-width: 1px;
}
.stroke-2 {
stroke-width: 2px;
}
.stroke-3 {
stroke-width: 3px;
}
.stroke-4 {
stroke-width: 4px;
}
/* --------------------------------
Caps/Corners
-------------------------------- */
.icon use {
--icon-stroke-linecap-butt: butt;
stroke-miterlimit: 10;
stroke-linecap: square;
stroke-linejoin: miter;
}
.stroke-round use {
--icon-stroke-linecap-butt: round;
stroke-linecap: round;
stroke-linejoin: round;
}
/* --------------------------------
Transformations/Animations
-------------------------------- */
.icon-rotate-90 {
transform: rotate(90deg);
}
.icon-rotate-180 {
transform: rotate(180deg);
}
.icon-rotate-270 {
transform: rotate(270deg);
}
.icon-flip-y {
transform: scaleY(-1);
}
.icon-flip-x {
transform: scaleX(-1);
}
.icon-is-spinning {
animation: icon-spin 1s infinite linear;
}
@keyframes icon-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View file

@ -2,8 +2,40 @@
{% block page_title %}CCCHH Dooris{% endblock %}
{% block page_body %}
<div id="locks"></div>
<div>
<p>Click the respective buttons to lock or unlock a door. If the status of a door is ”unknown”, you will need to
run the lock manually by pressing the lock or unlock button on the door lock.</p>
<div class="legend">
<p>Click the respective buttons to lock or unlock a door.</p>
<ul class="legend__icons">
<li>
<svg class="icon">
<use href="static/icons.svg#icon-lock-alt-svgrepo-com"/>
</svg>
The door is locked.
</li>
<li>
<svg class="icon">
<use href="static/icons.svg#icon-unlock-alt-svgrepo-com"/>
</svg>
The door is unlocked.
</li>
<li>
<svg class="icon">
<use href="static/icons.svg#icon-arrow-spin-svgrepo-com"/>
</svg>
The lock status is moving or unknown. If the status does not change within 30 seconds, you will need to
run the lock manually by pressing the lock or unlock button on the door lock.
</li>
<li>
<svg class="icon">
<use href="static/icons.svg#icon-mobile-bolt-svgrepo-com"/>
</svg>
The battery is getting low. Please open the door motor and swap out the batteries.
</li>
<li>
<svg class="icon">
<use href="static/icons.svg#icon-broken-link-svgrepo-com"/>
</svg>
The lock is offline. Verify the lock batteries and the CCU radio configuration.
</li>
</ul>
</div>
{% endblock %}