|
@@ -47,8 +47,19 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
return this._hass.states[`${base}_${suffix}`];
|
|
return this._hass.states[`${base}_${suffix}`];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ _setPending(delta) {
|
|
|
|
|
+ this._pending = (this._pending ?? 0) + delta;
|
|
|
|
|
+ this.shadowRoot?.getElementById("pending-indicator")
|
|
|
|
|
+ ?.classList.toggle("active", this._pending > 0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
async _callService(service, data) {
|
|
async _callService(service, data) {
|
|
|
- await this._hass.callService(DOMAIN, service, data);
|
|
|
|
|
|
|
+ this._setPending(1);
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this._hass.callService(DOMAIN, service, data);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this._setPending(-1);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
_render() {
|
|
_render() {
|
|
@@ -62,7 +73,12 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
this.shadowRoot.innerHTML = `
|
|
this.shadowRoot.innerHTML = `
|
|
|
<style>
|
|
<style>
|
|
|
:host { display: block; }
|
|
:host { display: block; }
|
|
|
|
|
+ ha-card { position: relative; }
|
|
|
.card-content { padding: 16px; }
|
|
.card-content { padding: 16px; }
|
|
|
|
|
+ .pending-indicator { position: absolute; top: 10px; right: 12px; opacity: 0; transition: opacity 0.2s; pointer-events: none; }
|
|
|
|
|
+ .pending-indicator.active { opacity: 1; }
|
|
|
|
|
+ .pending-indicator ha-icon { display: block; --mdc-icon-size: 20px; color: var(--primary-color); animation: spin 1s linear infinite; }
|
|
|
|
|
+ @keyframes spin { to { transform: rotate(360deg); } }
|
|
|
h3 { margin: 0 0 12px; font-size: 1rem; font-weight: 600; }
|
|
h3 { margin: 0 0 12px; font-size: 1rem; font-weight: 600; }
|
|
|
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
|
|
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
|
|
|
td { padding: 4px 8px; vertical-align: middle; }
|
|
td { padding: 4px 8px; vertical-align: middle; }
|
|
@@ -102,6 +118,7 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
.name-display { cursor: text; }
|
|
.name-display { cursor: text; }
|
|
|
</style>
|
|
</style>
|
|
|
<ha-card${this._config.title ? ` header="${this._config.title}"` : ""}>
|
|
<ha-card${this._config.title ? ` header="${this._config.title}"` : ""}>
|
|
|
|
|
+ <div class="pending-indicator" id="pending-indicator"><ha-icon icon="mdi:loading"></ha-icon></div>
|
|
|
<div class="card-content">
|
|
<div class="card-content">
|
|
|
<div class="section">
|
|
<div class="section">
|
|
|
<div class="status-row">
|
|
<div class="status-row">
|
|
@@ -260,13 +277,11 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
if (!slot || slot < 1) { status.textContent = "Enter a valid slot number."; return; }
|
|
if (!slot || slot < 1) { status.textContent = "Enter a valid slot number."; return; }
|
|
|
if (code.length < 4 || code.length > 8) { status.textContent = "PIN must be 4–8 digits."; return; }
|
|
if (code.length < 4 || code.length > 8) { status.textContent = "PIN must be 4–8 digits."; return; }
|
|
|
|
|
|
|
|
- status.textContent = "Setting…";
|
|
|
|
|
|
|
+ status.textContent = "";
|
|
|
try {
|
|
try {
|
|
|
await this._callService("set_code", { lock_entity_id: lock, slot, code });
|
|
await this._callService("set_code", { lock_entity_id: lock, slot, code });
|
|
|
slotInput.value = "";
|
|
slotInput.value = "";
|
|
|
codeInput.value = "";
|
|
codeInput.value = "";
|
|
|
- status.textContent = "Done.";
|
|
|
|
|
- setTimeout(() => { status.textContent = ""; }, 3000);
|
|
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
status.textContent = `Error: ${err.message ?? err}`;
|
|
status.textContent = `Error: ${err.message ?? err}`;
|
|
|
}
|
|
}
|