Initial commit: Hermes Agent Skills collection
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
# HA Lovelace Dashboard Enumeration via WebSocket API
|
||||
|
||||
Enumerating and reading HA Lovelace dashboard configurations requires the **WebSocket API**, not REST. The REST endpoints (`/api/config/dashboard/config`, `/api/lovelace/config`) return **404** — they do not exist.
|
||||
|
||||
## Listing All Dashboards
|
||||
|
||||
```javascript
|
||||
(async () => {
|
||||
const ha = document.querySelector("home-assistant");
|
||||
const conn = ha.hass.connection;
|
||||
|
||||
const dashboards = await conn.sendMessagePromise({
|
||||
type: 'lovelace/dashboards/list'
|
||||
});
|
||||
|
||||
// Returns array of: { id, title, url_path, icon, mode, require_admin, show_in_sidebar }
|
||||
return JSON.stringify(dashboards, null, 2);
|
||||
})()
|
||||
```
|
||||
|
||||
## Reading a Dashboard's Full Config (Views + Cards)
|
||||
|
||||
```javascript
|
||||
(async () => {
|
||||
const ha = document.querySelector("home-assistant");
|
||||
const conn = ha.hass.connection;
|
||||
|
||||
// Use url_path (the URL slug), NOT the dashboard id
|
||||
// e.g. 'dashboard-haus', 'stromerzeugung-verbrauch', 'lovelace' (default)
|
||||
const cfg = await conn.sendMessagePromise({
|
||||
type: 'lovelace/config',
|
||||
url_path: 'dashboard-haus' // ← URL slug from dashboards/list
|
||||
});
|
||||
|
||||
// Returns: { title, views: [{ title, path, icon, cards: [...], badges: [...] }] }
|
||||
// Empty dashboards return { views: [] }
|
||||
return JSON.stringify(cfg, null, 2);
|
||||
})()
|
||||
```
|
||||
|
||||
## Key Details
|
||||
|
||||
| Parameter | Correct key | Wrong key | Error if wrong |
|
||||
|-----------|------------|-----------|----------------|
|
||||
| Dashboard selector | `url_path` | `url` | `"extra keys not allowed @ data['url']"` |
|
||||
| Dashboard value | URL slug (e.g. `dashboard-haus`) | Dashboard ID (e.g. `dashboard_haus`) | Various |
|
||||
|
||||
- The default overview dashboard uses `url_path: 'lovelace'`.
|
||||
- `sendMessagePromise` is the async wrapper for WebSocket commands on `conn`. It returns a Promise that resolves to the response object.
|
||||
- All dashboards in this homelab use `mode: 'storage'` (managed via UI, not YAML files).
|
||||
- The REST API endpoints `/api/lovelace/config/{url}` and `/api/config/dashboard/config` both return 404. Only the WebSocket API works.
|
||||
|
||||
## Enumerating Card Types Across All Dashboards
|
||||
|
||||
Useful for auditing dashboard complexity (e.g. finding overloaded views):
|
||||
|
||||
```javascript
|
||||
(async () => {
|
||||
const ha = document.querySelector("home-assistant");
|
||||
const conn = ha.hass.connection;
|
||||
|
||||
const dashboards = await conn.sendMessagePromise({type: 'lovelace/dashboards/list'});
|
||||
const report = {};
|
||||
|
||||
for (const dash of dashboards) {
|
||||
try {
|
||||
const cfg = await conn.sendMessagePromise({
|
||||
type: 'lovelace/config',
|
||||
url_path: dash.url_path
|
||||
});
|
||||
const views = (cfg.views || []).map(v => ({
|
||||
title: v.title,
|
||||
path: v.path,
|
||||
icon: v.icon,
|
||||
cardCount: (v.cards || []).length,
|
||||
cardTypes: (v.cards || []).map(c => c.type).filter(Boolean)
|
||||
}));
|
||||
report[dash.url_path] = { title: dash.title, views };
|
||||
} catch(e) {
|
||||
report[dash.url_path] = { error: String(e).substring(0, 100) };
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(report, null, 2);
|
||||
})()
|
||||
```
|
||||
|
||||
## Writing Dashboard Config Back
|
||||
|
||||
To modify a dashboard, send the full config object back via WebSocket:
|
||||
|
||||
```javascript
|
||||
(async () => {
|
||||
const ha = document.querySelector("home-assistant");
|
||||
const conn = ha.hass.connection;
|
||||
|
||||
// Read current config
|
||||
const cfg = await conn.sendMessagePromise({
|
||||
type: 'lovelace/config',
|
||||
url_path: 'dashboard-haus'
|
||||
});
|
||||
|
||||
// Modify (e.g. add a view, rearrange cards)
|
||||
cfg.views.push({
|
||||
title: 'New View',
|
||||
path: 'new-view',
|
||||
cards: [{ type: 'entities', entities: ['sensor.example'], title: 'Example' }]
|
||||
});
|
||||
|
||||
// Save back
|
||||
await conn.sendMessagePromise({
|
||||
type: 'lovelace/config/save',
|
||||
url_path: 'dashboard-haus',
|
||||
config: cfg
|
||||
});
|
||||
return 'Saved';
|
||||
})()
|
||||
```
|
||||
|
||||
## This Homelab's Dashboard Inventory (as of 2026-07-10, post-redesign)
|
||||
|
||||
| Dashboard | URL Path | Views | Cards (main view) | Status |
|
||||
|-----------|----------|-------|--------------------|--------|
|
||||
| Haus (NEW) | `dashboard-haus` | 5 | 6/3/5/4/5 | ✅ Mushroom + Bubble, mobile-first |
|
||||
| Stromerzeugung/-verbrauch | `stromerzeugung-verbrauch` | 5 | 30/1/4/4/1 | ✅ All views filled |
|
||||
| Übersicht (backup) | `lovelace` | 3 | 43+1+1 | Old overview, kept as backup |
|
||||
| IT | `dashboard-it` | 1 | 16 | OK |
|
||||
| Karte | `map` | 1 | 1 | Minimal |
|
||||
| Editor | `dashboard-editor` | 1 | 1 | Tool (config-editor-card) |
|
||||
|
||||
Deleted (2026-07-10): dashboard-test, Typen, Home_Auto, Moonraker (4 empty ghost dashboards).
|
||||
|
||||
Custom cards installed: `power-flow-card-plus`, `price-timeline-card`, `config-editor-card`, `platinum-weather-card`, `xiaomi-vacuum-map-card`, `apexcharts-card`, `kiosk-mode`, `mushroom` (v5.1.1), `bubble-card` (v3.2.4).
|
||||
|
||||
## Deleting Dashboards
|
||||
|
||||
```javascript
|
||||
await conn.sendMessagePromise({
|
||||
type: 'lovelace/dashboards/delete',
|
||||
dashboard_id: 'dashboard_test' // ← underscore ID from dashboards/list
|
||||
});
|
||||
// Returns: null on success
|
||||
```
|
||||
|
||||
**Key:** `dashboard_id` uses underscores (e.g. `dashboard_test`), while `lovelace/config` uses `url_path` with hyphens (e.g. `dashboard-test`). Always use `id` from the `dashboards/list` response for deletion.
|
||||
|
||||
## Listing Lovelace Resources (Custom Cards)
|
||||
|
||||
```javascript
|
||||
const resources = await conn.sendMessagePromise({type: 'lovelace/resources'});
|
||||
// Returns: [{id, url, type}]
|
||||
// HACS auto-registers installed cards here. Example:
|
||||
// /hacsfiles/lovelace-mushroom/mushroom.js?hacstag=444350375511
|
||||
// /hacsfiles/Bubble-Card/bubble-card.js?hacstag=680112919324
|
||||
```
|
||||
|
||||
Use this to verify HACS installations landed correctly, or to audit which custom cards are available for dashboard configs.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **REST API 404 for dashboard config.** Neither `/api/lovelace/config` nor `/api/config/dashboard/config` exist as REST endpoints. Use WebSocket `lovelace/config` with `url_path` parameter.
|
||||
- **`url` vs `url_path` parameter name.** Using `url` instead of `url_path` causes `"extra keys not allowed @ data['url']"` error.
|
||||
- **Empty dashboards return `{ views: [] }`, not an error.** Don't treat missing views as a failure — the dashboard exists but has no content.
|
||||
- **Dashboard ID uses underscores, URL path uses hyphens.** `dashboard_haus` (id) → `dashboard-haus` (url_path). Always use `url_path` for `lovelace/config` queries, but `dashboard_id` (underscore form) for `lovelace/dashboards/delete`.
|
||||
- **`sendMessagePromise` not `sendMessage`.** The plain `sendMessage` is fire-and-forget. Use `sendMessagePromise` to get the response.
|
||||
- **Verify entity IDs before saving.** Query `ha.hass.states` for each entity ID before writing a dashboard config. Scene names may differ (`scene.alles_aus_2` not `scene.alles_aus`), binary sensors may lack `device_class` (filter by name keywords), and some sensors may be `unavailable`. Dead entity references produce "Entity not available" cards.
|
||||
Reference in New Issue
Block a user