|
|
@@ -25,7 +25,15 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
}
|
|
|
|
|
|
set hass(hass) {
|
|
|
+ const prev = this._hass;
|
|
|
this._hass = hass;
|
|
|
+ if (!this._config) return;
|
|
|
+
|
|
|
+ const entity = this._config.entity;
|
|
|
+ const base = entity.replace(/_slots$/, "");
|
|
|
+ const watched = [entity, `${base}_last_action`, `${base}_last_user`];
|
|
|
+ if (prev && watched.every(e => prev.states[e] === hass.states[e])) return;
|
|
|
+
|
|
|
this._render();
|
|
|
}
|
|
|
|
|
|
@@ -60,19 +68,24 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
th { text-align: left; padding: 4px 8px; opacity: 0.6; font-weight: 500; }
|
|
|
td { padding: 4px 8px; vertical-align: middle; }
|
|
|
tr:not(:last-child) td { border-bottom: 1px solid var(--divider-color, #e0e0e0); }
|
|
|
- .badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 0.75rem; font-weight: 600; }
|
|
|
- .badge.enabled { background: var(--success-color, #4caf50); color: #fff; }
|
|
|
- .badge.disabled { background: var(--warning-color, #ff9800); color: #fff; }
|
|
|
+ .status-icon ha-icon { --mdc-icon-size: 20px; }
|
|
|
+ .status-icon.enabled ha-icon { color: var(--success-color, #4caf50); }
|
|
|
+ .status-icon.disabled ha-icon { color: var(--warning-color, #ff9800); }
|
|
|
.name-cell { display: flex; align-items: center; gap: 6px; }
|
|
|
- .edit-btn { background: none; border: none; cursor: pointer; padding: 2px; opacity: 0.5; font-size: 0.9rem; }
|
|
|
- .edit-btn:hover { opacity: 1; }
|
|
|
+ .icon-btn { background: none; border: none; cursor: pointer; padding: 2px; opacity: 0.5; display: flex; align-items: center; }
|
|
|
+ .icon-btn:hover { opacity: 1; }
|
|
|
+ .icon-btn ha-icon { --mdc-icon-size: 18px; }
|
|
|
+ .clear-btn ha-icon { color: var(--error-color, #f44336); --mdc-icon-size: 20px; }
|
|
|
.section { margin-top: 20px; }
|
|
|
.form-row { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
|
|
|
+ .form-row-inline { display: flex; gap: 10px; margin-bottom: 10px; }
|
|
|
+ .form-row-inline .field { display: flex; flex-direction: column; gap: 4px; }
|
|
|
+ .form-row-inline .field.slot { width: 80px; }
|
|
|
+ .form-row-inline .field.pin { width: 130px; }
|
|
|
label { font-size: 0.8rem; opacity: 0.7; }
|
|
|
- input { padding: 6px 8px; border: 1px solid var(--divider-color, #ccc); border-radius: 4px; font-size: 0.9rem; background: var(--card-background-color, #fff); color: var(--primary-text-color); }
|
|
|
+ input { padding: 6px 8px; border: 1px solid var(--divider-color, #ccc); border-radius: 4px; font-size: 0.9rem; background: var(--card-background-color, #fff); color: var(--primary-text-color); width: 100%; box-sizing: border-box; }
|
|
|
button.action { background: var(--primary-color, #03a9f4); color: #fff; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; font-size: 0.9rem; }
|
|
|
button.action:hover { opacity: 0.85; }
|
|
|
- button.danger { background: var(--error-color, #f44336); }
|
|
|
.status-row { display: flex; gap: 24px; font-size: 0.85rem; margin-bottom: 4px; }
|
|
|
.status-label { opacity: 0.6; }
|
|
|
.empty-note { opacity: 0.5; font-size: 0.85rem; padding: 8px 0; }
|
|
|
@@ -97,20 +110,28 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
${slots.map(s => `
|
|
|
<tr data-slot="${s.slot_num}">
|
|
|
<td>${s.slot_num}</td>
|
|
|
- <td><span class="badge ${s.status}">${s.status}</span></td>
|
|
|
+ <td>
|
|
|
+ <span class="status-icon ${s.status}">
|
|
|
+ <ha-icon icon="${s.status === "enabled" ? "mdi:account" : "mdi:account-off"}"></ha-icon>
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
<td>
|
|
|
<span class="name-cell" id="name-display-${s.slot_num}">
|
|
|
${s.name ?? '<em style="opacity:.4">unnamed</em>'}
|
|
|
- <button class="edit-btn" data-action="edit-name" data-slot="${s.slot_num}" title="Edit name">✏️</button>
|
|
|
+ <button class="icon-btn" data-action="edit-name" data-slot="${s.slot_num}" title="Edit name">
|
|
|
+ <ha-icon icon="mdi:account-edit"></ha-icon>
|
|
|
+ </button>
|
|
|
</span>
|
|
|
<span class="inline-edit" id="name-edit-${s.slot_num}" style="display:none">
|
|
|
<input id="name-input-${s.slot_num}" type="text" value="${s.name ?? ''}" placeholder="Enter name" />
|
|
|
<button class="action" data-action="save-name" data-slot="${s.slot_num}">Save</button>
|
|
|
- <button class="edit-btn" data-action="cancel-name" data-slot="${s.slot_num}">✕</button>
|
|
|
+ <button class="icon-btn" data-action="cancel-name" data-slot="${s.slot_num}">✕</button>
|
|
|
</span>
|
|
|
</td>
|
|
|
<td>
|
|
|
- <button class="action danger" data-action="clear" data-slot="${s.slot_num}" style="padding:4px 10px;font-size:0.8rem">Clear</button>
|
|
|
+ <button class="icon-btn clear-btn" data-action="clear" data-slot="${s.slot_num}" title="Clear slot ${s.slot_num}">
|
|
|
+ <ha-icon icon="mdi:account-remove"></ha-icon>
|
|
|
+ </button>
|
|
|
</td>
|
|
|
</tr>
|
|
|
`).join("")}
|
|
|
@@ -121,13 +142,15 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
|
|
|
<div class="section">
|
|
|
<h3>Add / overwrite slot</h3>
|
|
|
- <div class="form-row">
|
|
|
- <label>Slot number</label>
|
|
|
- <input id="add-slot" type="number" min="1" placeholder="e.g. 5" />
|
|
|
- </div>
|
|
|
- <div class="form-row">
|
|
|
- <label>Code (PIN)</label>
|
|
|
- <input id="add-code" type="password" placeholder="PIN code" />
|
|
|
+ <div class="form-row-inline">
|
|
|
+ <div class="field slot">
|
|
|
+ <label>Slot #</label>
|
|
|
+ <input id="add-slot" type="number" min="1" max="999" placeholder="e.g. 5" />
|
|
|
+ </div>
|
|
|
+ <div class="field pin">
|
|
|
+ <label>PIN</label>
|
|
|
+ <input id="add-code" type="password" minlength="4" maxlength="8" placeholder="4–8 digits" />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div class="form-row">
|
|
|
<label>Name (optional)</label>
|
|
|
@@ -184,7 +207,7 @@ class ZLockSlotsCard extends HTMLElement {
|
|
|
const name = nameInput.value.trim() || undefined;
|
|
|
|
|
|
if (!slot || slot < 1) { status.textContent = "Enter a valid slot number."; return; }
|
|
|
- if (!code) { status.textContent = "Enter a PIN code."; return; }
|
|
|
+ if (code.length < 4 || code.length > 8) { status.textContent = "PIN must be 4–8 digits."; return; }
|
|
|
|
|
|
status.textContent = "Setting…";
|
|
|
try {
|