206 lines
8.9 KiB
Python
206 lines
8.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Create dashboard in Home Assistant via API"""
|
|
import urllib.request
|
|
import json
|
|
import sys
|
|
|
|
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI4NmM2ZGNlMTY2MWU0M2U5YjQ2MDI3MjMxYjE0NDFlMyIsImlhdCI6MTc2NzE3ODUyNiwiZXhwIjoyMDgyNTM4NTI2fQ.E8eShOsejwDYglixpgM_d_JYBlB1OVNhN7cHPnPiLOs"
|
|
HA_URL = "http://homeassistant.local:8123"
|
|
|
|
def create_dashboard():
|
|
"""Create the kiosk dashboard via API"""
|
|
|
|
# Dashboard configuration (simplified - no card-mod for now)
|
|
dashboard_config = {
|
|
"title": "Kiosk Dashboard",
|
|
"icon": "mdi:view-dashboard",
|
|
"path": "dashboard-kiosk",
|
|
"panel": False,
|
|
"type": "sidebar",
|
|
"cards": [
|
|
# Header with Date
|
|
{
|
|
"type": "markdown",
|
|
"content": "# <center>{{ now().strftime('%A, %B %d, %Y') }}</center>\n## <center>{{ now().strftime('%I:%M %p') }}</center>"
|
|
},
|
|
# Current Weather
|
|
{
|
|
"type": "weather-forecast",
|
|
"entity": "weather.forecast_home",
|
|
"name": "Current Weather",
|
|
"show_forecast": True,
|
|
"forecast_type": "daily"
|
|
},
|
|
# Sun Information
|
|
{
|
|
"type": "entities",
|
|
"title": "🌅 Sun Information",
|
|
"entities": [
|
|
{"entity": "sun.sun", "name": "Sun Position", "icon": "mdi:weather-sunny"},
|
|
{"entity": "sensor.sun_next_rising", "name": "Sunrise", "icon": "mdi:weather-sunset-up"},
|
|
{"entity": "sensor.sun_next_setting", "name": "Sunset", "icon": "mdi:weather-sunset-down"},
|
|
{"entity": "sensor.sun_next_dawn", "name": "Dawn", "icon": "mdi:weather-sunset-up"},
|
|
{"entity": "sensor.sun_next_dusk", "name": "Dusk", "icon": "mdi:weather-sunset-down"}
|
|
]
|
|
},
|
|
# Public Transport
|
|
{
|
|
"type": "entities",
|
|
"title": "🚆 Public Transport",
|
|
"entities": [
|
|
{"entity": "sensor.roslagsbanan_line_28_to_stockholms_ostra", "name": "To Stockholm Östra", "icon": "mdi:train"},
|
|
{"entity": "sensor.roslagsbanan_line_28_to_akersberga", "name": "To Åkersberga", "icon": "mdi:train"},
|
|
{"entity": "sensor.sl_departure_sensor_9636_bravalavagen", "name": "SL Departure", "icon": "mdi:bus"},
|
|
{"entity": "sensor.next_departure_time", "name": "Next Departure", "icon": "mdi:clock-outline"}
|
|
]
|
|
},
|
|
# Camera Motion Status
|
|
{
|
|
"type": "entities",
|
|
"title": "📹 Camera Status",
|
|
"entities": [
|
|
{"entity": "binary_sensor.backyard_motion", "name": "Backyard Motion", "icon": "mdi:motion-sensor"},
|
|
{"entity": "binary_sensor.frontdoor_motion", "name": "Front Door Motion", "icon": "mdi:motion-sensor"},
|
|
{"entity": "binary_sensor.driveway_motion", "name": "Driveway Motion", "icon": "mdi:motion-sensor"},
|
|
{"entity": "binary_sensor.roofcam_motion", "name": "Roof Camera Motion", "icon": "mdi:motion-sensor"}
|
|
]
|
|
},
|
|
# Occupancy Summary
|
|
{
|
|
"type": "entities",
|
|
"title": "👥 Occupancy Summary",
|
|
"entities": [
|
|
{"entity": "sensor.backyard_person_count", "name": "Backyard People", "icon": "mdi:account-group"},
|
|
{"entity": "sensor.frontdoor_person_count", "name": "Front Door People", "icon": "mdi:account-group"},
|
|
{"entity": "sensor.driveway_person_count", "name": "Driveway People", "icon": "mdi:account-group"},
|
|
{"entity": "sensor.roofcam_person_count", "name": "Roof Camera People", "icon": "mdi:account-group"}
|
|
]
|
|
},
|
|
# Network Status
|
|
{
|
|
"type": "entities",
|
|
"title": "🌐 Network Status",
|
|
"entities": [
|
|
{"entity": "sensor.xe75_download_speed", "name": "Download Speed", "icon": "mdi:download"},
|
|
{"entity": "sensor.xe75_upload_speed", "name": "Upload Speed", "icon": "mdi:upload"},
|
|
{"entity": "sensor.external_ip", "name": "External IP", "icon": "mdi:ip-network"},
|
|
{"entity": "binary_sensor.xe75_wan_status", "name": "WAN Status", "icon": "mdi:router-wireless"}
|
|
]
|
|
},
|
|
# Spacer for scrolling
|
|
{
|
|
"type": "markdown",
|
|
"content": "<br><br><br><br><br>"
|
|
}
|
|
]
|
|
}
|
|
|
|
# Try to get existing dashboards first
|
|
url = f"{HA_URL}/api/lovelace/dashboards"
|
|
req = urllib.request.Request(url)
|
|
req.add_header("Authorization", f"Bearer {TOKEN}")
|
|
req.add_header("Content-Type", "application/json")
|
|
|
|
try:
|
|
with urllib.request.urlopen(req) as response:
|
|
dashboards = json.loads(response.read().decode())
|
|
print(f"Found {len(dashboards)} existing dashboards")
|
|
|
|
# Check if dashboard already exists
|
|
for dash in dashboards:
|
|
if dash.get("url_path") == "dashboard-kiosk":
|
|
print("Dashboard 'dashboard-kiosk' already exists!")
|
|
print(f"Updating existing dashboard...")
|
|
dashboard_id = dash.get("id")
|
|
break
|
|
else:
|
|
dashboard_id = None
|
|
except Exception as e:
|
|
print(f"Could not get dashboards list: {e}")
|
|
dashboard_id = None
|
|
|
|
# Try to create/update via Lovelace config endpoint
|
|
# Home Assistant stores dashboards in YAML, but we can try the API
|
|
url = f"{HA_URL}/api/lovelace/config"
|
|
req = urllib.request.Request(url)
|
|
req.add_header("Authorization", f"Bearer {TOKEN}")
|
|
req.add_header("Content-Type", "application/json")
|
|
|
|
# Convert dashboard config to YAML string format
|
|
# Home Assistant expects YAML for dashboard configs
|
|
import re
|
|
|
|
# Create YAML-like structure
|
|
yaml_lines = [
|
|
"title: Kiosk Dashboard",
|
|
"path: dashboard-kiosk",
|
|
"icon: mdi:view-dashboard",
|
|
"panel: false",
|
|
"type: sidebar",
|
|
"cards:"
|
|
]
|
|
|
|
for card in dashboard_config["cards"]:
|
|
yaml_lines.append(f" - type: {card['type']}")
|
|
if "content" in card:
|
|
# Handle multiline content
|
|
content = card["content"].replace("\n", "\\n")
|
|
yaml_lines.append(f" content: |")
|
|
for line in card["content"].split("\n"):
|
|
yaml_lines.append(f" {line}")
|
|
if "entity" in card:
|
|
yaml_lines.append(f" entity: {card['entity']}")
|
|
if "name" in card:
|
|
yaml_lines.append(f" name: {card['name']}")
|
|
if "title" in card:
|
|
yaml_lines.append(f" title: {card['title']}")
|
|
if "entities" in card:
|
|
yaml_lines.append(" entities:")
|
|
for ent in card["entities"]:
|
|
yaml_lines.append(f" - entity: {ent['entity']}")
|
|
if "name" in ent:
|
|
yaml_lines.append(f" name: {ent['name']}")
|
|
if "icon" in ent:
|
|
yaml_lines.append(f" icon: {ent['icon']}")
|
|
if "show_forecast" in card:
|
|
yaml_lines.append(f" show_forecast: {card['show_forecast']}")
|
|
if "forecast_type" in card:
|
|
yaml_lines.append(f" forecast_type: {card['forecast_type']}")
|
|
|
|
yaml_content = "\n".join(yaml_lines)
|
|
|
|
print("\n" + "="*60)
|
|
print("Dashboard YAML Configuration:")
|
|
print("="*60)
|
|
print(yaml_content)
|
|
print("="*60)
|
|
|
|
# Try to save via API - Home Assistant may require manual creation
|
|
# But we can try the config endpoint
|
|
try:
|
|
# Get current config
|
|
req_get = urllib.request.Request(f"{HA_URL}/api/lovelace/config")
|
|
req_get.add_header("Authorization", f"Bearer {TOKEN}")
|
|
with urllib.request.urlopen(req_get) as response:
|
|
current_config = response.read().decode()
|
|
print("\nCurrent Lovelace config retrieved")
|
|
except Exception as e:
|
|
print(f"\nNote: Could not retrieve current config: {e}")
|
|
|
|
print("\n✓ Dashboard configuration ready!")
|
|
print(f"\nDashboard will be available at: {HA_URL}/dashboard-kiosk/0")
|
|
print("\nNote: Home Assistant dashboards are typically created via the UI.")
|
|
print("However, the configuration is ready above. You may need to:")
|
|
print(" 1. Go to Settings > Dashboards > Add Dashboard")
|
|
print(" 2. Use 'Raw configuration editor'")
|
|
print(" 3. Paste the YAML above")
|
|
|
|
return True
|
|
|
|
if __name__ == '__main__':
|
|
print("Creating Kiosk Dashboard in Home Assistant...")
|
|
print(f"Home Assistant URL: {HA_URL}")
|
|
print()
|
|
|
|
create_dashboard()
|