feat: Home Assistant Benutzername/Passwort-Authentifizierung

Ergänzt die HA-Integration um Password-Grant OAuth2: Nutzer können sich
nun wahlweise mit einem Long-Lived Token oder mit Benutzername/Passwort
anmelden. Access Tokens werden automatisch per Refresh-Token erneuert.
This commit is contained in:
Scarriffle
2026-04-21 11:02:32 +02:00
parent 978ad55af4
commit 69f5789e2d
6 changed files with 178 additions and 11 deletions

View File

@@ -417,9 +417,30 @@
<input type="url" id="ha-account-url" placeholder="http://homeassistant.local:8123" />
</div>
<div class="form-group">
<label>Anmeldemethode</label>
<div style="display:flex;gap:16px">
<label class="toggle-label">
<input type="radio" name="ha-auth-method" value="token" id="ha-auth-token" checked /> Long-Lived Token
</label>
<label class="toggle-label">
<input type="radio" name="ha-auth-method" value="password" id="ha-auth-password" /> Benutzername/Passwort
</label>
</div>
</div>
<div class="form-group" id="ha-token-group">
<label>Long-Lived Access Token</label>
<input type="password" id="ha-account-token" placeholder="Token aus Profil → Sicherheit" autocomplete="off" />
</div>
<div id="ha-credentials-group" class="hidden">
<div class="form-group">
<label>Benutzername</label>
<input type="text" id="ha-account-username" autocomplete="username" />
</div>
<div class="form-group">
<label>Passwort</label>
<input type="password" id="ha-account-userpass" autocomplete="current-password" />
</div>
</div>
<div id="ha-account-error" class="form-error hidden"></div>
</div>
<div class="modal-footer">

View File

@@ -1266,28 +1266,65 @@ function openHAAccountModal() {
document.getElementById('ha-account-name').value = '';
document.getElementById('ha-account-url').value = '';
document.getElementById('ha-account-token').value = '';
document.getElementById('ha-account-username').value = '';
document.getElementById('ha-account-userpass').value = '';
document.getElementById('ha-account-error').classList.add('hidden');
// Reset to token method
document.getElementById('ha-auth-token').checked = true;
document.getElementById('ha-token-group').classList.remove('hidden');
document.getElementById('ha-credentials-group').classList.add('hidden');
openModal('modal-ha-account');
}
function bindHAAccountModal() {
// Toggle auth method fields
document.querySelectorAll('[name="ha-auth-method"]').forEach(r => {
r.addEventListener('change', () => {
const isPw = document.getElementById('ha-auth-password').checked;
document.getElementById('ha-token-group').classList.toggle('hidden', isPw);
document.getElementById('ha-credentials-group').classList.toggle('hidden', !isPw);
});
});
document.getElementById('ha-account-save').onclick = async () => {
const name = document.getElementById('ha-account-name').value.trim();
const url = document.getElementById('ha-account-url').value.trim();
const token = document.getElementById('ha-account-token').value.trim();
const isPw = document.getElementById('ha-auth-password').checked;
const errEl = document.getElementById('ha-account-error');
if (!name || !url || !token) {
errEl.textContent = 'Bitte alle Felder ausfüllen';
if (!name || !url) {
errEl.textContent = 'Bitte Name und URL ausfüllen';
errEl.classList.remove('hidden');
return;
}
const body = { name, url };
if (isPw) {
const username = document.getElementById('ha-account-username').value.trim();
const password = document.getElementById('ha-account-userpass').value.trim();
if (!username || !password) {
errEl.textContent = 'Bitte Benutzername und Passwort ausfüllen';
errEl.classList.remove('hidden');
return;
}
body.username = username;
body.password = password;
} else {
const token = document.getElementById('ha-account-token').value.trim();
if (!token) {
errEl.textContent = 'Bitte Access Token ausfüllen';
errEl.classList.remove('hidden');
return;
}
body.token = token;
}
errEl.classList.add('hidden');
const saveBtn = document.getElementById('ha-account-save');
saveBtn.disabled = true;
saveBtn.textContent = 'Verbinde…';
try {
const account = await api.post('/homeassistant/accounts', { name, url, token });
const account = await api.post('/homeassistant/accounts', body);
state.haAccounts.push(account);
renderCalendarList();
closeModal('modal-ha-account');