Creates and updates Home Assistant Lovelace dashboards programmatically via WebSocket API with dashboard structure, view configuration, and entity validation.
Works with Python websocket-client, WebSocket API authentication, and Lovelace configuration.
Create and update Lovelace dashboards programmatically using the WebSocket API.
Home Assistant requires dashboard URL paths to contain a hyphen:
Rule: Always use kebab-case with at least one hyphen in the url_path field.
Use this skill when you need to:
Do NOT use when:
lovelace/dashboards/listlovelace/dashboards/create with url_path containing hyphenlovelace/config/saveimport json
import websocket
HA_URL = "http://192.168.68.123:8123"
HA_TOKEN = os.environ["HA_LONG_LIVED_TOKEN"]
def create_dashboard(url_path: str, title: str, config: dict):
"""Create or update a dashboard.
Args:
url_path: Dashboard URL path (must contain hyphen, e.g., "climate-control")
title: Dashboard display title
config: Dashboard configuration dict
"""
# Validate url_path contains hyphen
if "-" not in url_path:
raise ValueError(f"url_path must contain hyphen: '{url_path}' -> '{url_path}-view'")
ws_url = HA_URL.replace("http://", "ws://") + "/api/websocket"
ws = websocket.create_connection(ws_url)
msg_id = 1
# 1. Receive auth_required
ws.recv()
# 2. Send auth
ws.send(json.dumps({"type": "auth", "access_token": HA_TOKEN}))
ws.recv() # auth_ok
# 3. Check if dashboard exists
ws.send(json.dumps({"id": msg_id, "type": "lovelace/dashboards/list"}))
msg_id += 1
response = json.loads(ws.recv())
exists = any(d["url_path"] == url_path for d in response.get("result", []))
# 4. Create if doesn't exist
if not exists:
ws.send(json.dumps({
"id": msg_id,
"type": "lovelace/dashboards/create",
"url_path": url_path, # Must contain hyphen!
"title": title,
"icon": "mdi:view-dashboard",
"show_in_sidebar": True,
}))
msg_id += 1
ws.recv()
# 5. Save configuration
ws.send(json.dumps({
"id": msg_id,
"type": "lovelace/config/save",
"url_path": url_path,
"config": config,
}))
ws.recv()
ws.close()
| Type | Purpose |
|---|---|
lovelace/dashboards/list |
List all dashboards |
lovelace/dashboards/create |
Create new dashboard |
lovelace/dashboards/delete |
Delete dashboard |
lovelace/config/save |
Save dashboard config |
lovelace/config |
Get dashboard config |
system_log/list |
Check for lovelace errors |
See references/card-types.md for dashboard configuration structure and common card types.
# 1. Check system logs for lovelace errors
ws.send(json.dumps({"id": 1, "type": "system_log/list"}))
logs = json.loads(ws.recv())
# Filter for 'lovelace' or 'frontend' errors
# 2. Validate dashboard configuration
ws.send(json.dumps({
"id": 2,
"type": "lovelace/config",
"url_path": "climate-control" # Must contain hyphen
}))
config = json.loads(ws.recv())
# 3. Validate entity IDs exist
ws.send(json.dumps({"id": 3, "type": "get_states"}))
states = json.loads(ws.recv())
entity_ids = [s["entity_id"] for s in states["result"]]
# 4. Check if entities used in dashboard exist
for card in dashboard_config["views"][0]["cards"]:
if "entity" in card:
if card["entity"] not in entity_ids:
print(f"Warning: Entity {card['entity']} not found")
"url_path": "climate" → Add hyphen: "climate-control"Verify entity IDs exist before using them in dashboards.
See references/entity-patterns.md for entity validation functions and common entity patterns.
| Dashboard Type | Bad URL Path | Good URL Path |
|---|---|---|
| Climate monitoring | "climate" | "climate-control" |
| Mobile view | "mobile" | "mobile-app" |
| Energy tracking | "energy" | "energy-monitor" |
| Air quality | "air" | "air-quality" |
| Irrigation | "irrigation" | "irrigation-control" |
show_in_sidebar: True in dashboard creationurl_path contains hyphenurl_path matches existing dashboardsystem_log/list{"type": "get_states"}state_class for sensors