feat: Home Assistant settings UI + fix channels.json
notifications.html: add Home Assistant section with two collapsible blocks — Connection (HA URL + Long-Lived Access Token) and Inbound webhook (webhook ID with endpoint URL hint showing the username). Token field uses keep-existing pattern (blank = no change). settings.py: wire ha_url, ha_token, ha_webhook_id through _notifications_page() template substitution and save_notifications() POST handler. Preserves existing HA config fields (persona, tier, role, tools) on save. TODO__Agents.md: add Home Assistant integration planning section (event design, richer payload template, HA API tools). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,9 @@ def _notifications_page(username: str, back_persona: str = "", success: str = ""
|
||||
nc_username = _html.escape(nct.get("nc_username", "") or "")
|
||||
nc_app_password = _html.escape(nct.get("nc_app_password", "") or "")
|
||||
gc_webhook = _html.escape((channels.get("google_chat") or {}).get("outbound_webhook", "") or "")
|
||||
ha = channels.get("homeassistant") or {}
|
||||
ha_url = _html.escape(ha.get("url", "") or "")
|
||||
ha_webhook_id = _html.escape(ha.get("webhook_id", "") or "")
|
||||
|
||||
html = html.replace("{{ notify_channel }}", notify_ch)
|
||||
html = html.replace("{{ notify_email_override }}", notify_email)
|
||||
@@ -76,6 +79,9 @@ def _notifications_page(username: str, back_persona: str = "", success: str = ""
|
||||
html = html.replace("{{ nc_username }}", nc_username)
|
||||
html = html.replace("{{ nc_app_password }}", nc_app_password)
|
||||
html = html.replace("{{ gc_webhook }}", gc_webhook)
|
||||
html = html.replace("{{ ha_url }}", ha_url)
|
||||
html = html.replace("{{ ha_webhook_id }}", ha_webhook_id)
|
||||
html = html.replace("{{ ha_username }}", username)
|
||||
html = html.replace("{{ back_href }}", f"/{username}/{back_persona}" if back_persona else "/")
|
||||
html = html.replace("{{ help_href }}", f"/help?persona={back_persona}" if back_persona else "/help")
|
||||
if success:
|
||||
@@ -309,6 +315,9 @@ async def save_notifications(
|
||||
nc_username: str = Form(""),
|
||||
nc_app_password: str = Form(""),
|
||||
gc_outbound_webhook: str = Form(""),
|
||||
ha_url: str = Form(""),
|
||||
ha_token: str = Form(""),
|
||||
ha_webhook_id: str = Form(""),
|
||||
):
|
||||
username = _get_session_user(request)
|
||||
if not username:
|
||||
@@ -356,6 +365,17 @@ async def save_notifications(
|
||||
channels["google_chat"] = {}
|
||||
channels["google_chat"]["outbound_webhook"] = gc_outbound_webhook.strip()
|
||||
|
||||
# Home Assistant — nested under "homeassistant"
|
||||
if "homeassistant" not in channels:
|
||||
channels["homeassistant"] = {}
|
||||
ha = channels["homeassistant"]
|
||||
if ha_url.strip():
|
||||
ha["url"] = ha_url.strip().rstrip("/")
|
||||
if ha_token.strip():
|
||||
ha["token"] = ha_token.strip()
|
||||
if ha_webhook_id.strip():
|
||||
ha["webhook_id"] = ha_webhook_id.strip()
|
||||
|
||||
channels_path.write_text(json.dumps(channels, indent=2) + "\n")
|
||||
logger.info("notifications updated for %s (channel=%s)", username, notification_channel or "none")
|
||||
return HTMLResponse(_notifications_page(username, back_persona, success="Notification settings saved."))
|
||||
|
||||
Reference in New Issue
Block a user