HA Widgets — Eigene Home-Assistant-Apps mit Vue 3 (Teil 2: Wie fängt man an?)

- Veröffentlicht unter Makerspace von

Hinweis: Der Beitrag wurde mit KI-Unterstützung (Claude Code) verfasst und manuell nachbearbeitet.

HA Widgets — alle Teile: Teil 1: Überblick · Teil 2: Wie fängt man an? · Teil 3: Architektur · Teil 4: Architektur-Checks · Teil 5: HA-Infrastruktur · Teil 6: LCARS-Design

Teil 1 hat gezeigt was am Ende dabei rauskommt. Dieser Teil beschreibt den Einstieg: Claude Code einrichten, erstes Vue-3-Projekt aufsetzen, in HA als iFrame einbinden, erste WebSocket-Verbindung.


Claude Code einrichten

Claude Code ist ein CLI-Tool von Anthropic — KI im Terminal, die direkt mit den Dateien im Projektordner arbeitet. Kein grafisches Interface, kein Klicken. Man gibt Aufgaben in natürlicher Sprache, Claude liest und schreibt Dateien, schlägt Änderungen vor.

Installation (Node.js vorausgesetzt):

npm install -g @anthropic-ai/claude-code

Beim ersten Start einloggen:

claude

Der Login-Flow führt durch die Authentifizierung mit einem Anthropic-Account. Danach ist es einsatzbereit.

Wie man damit sinnvoll arbeitet

Claude Code ist kein Code-Generator. Sinnvoll eingesetzt ist es ein Pair-Programmer: beschreiben was man will, Vorschlag prüfen, korrigieren oder akzeptieren. Das Wichtigste: verstehen was passiert. Vorschläge nicht blind übernehmen.

Was gut funktioniert: - Architektur besprechen bevor Code geschrieben wird ("ich will X umsetzen, wie würdest du das angehen?") - Kleine, konkrete Aufgaben ("füge eine Computed-Property hinzu die X berechnet") - Fehlermeldungen direkt hineinkopieren und erklären lassen - Regeln definieren die Claude einhalten muss

Was nicht funktioniert: - Zu vage Aufgaben ("bau mir eine App") - Vorschläge ohne Lesen übernehmen

CLAUDE.md — das Regelwerk

Claude Code liest beim Start eine CLAUDE.md-Datei im Projektordner. Dort stehen Regeln, die bei jeder Interaktion gelten. Beispiele aus diesem Projekt:

  • Kein any in TypeScript (einzige Ausnahme: catch (e: any))
  • Kein fetch() direkt in Vue-Komponenten — gehört in einen Composable
  • Keine hardcodierten Entity-IDs im Template
  • Nach jeder Änderung: npx tsc --noEmit in allen Apps

Ohne dieses Regelwerk driftet der Code über viele Sessions auseinander. Mit CLAUDE.md bleibt der Stil konsistent — Claude kennt die Regeln und hält sich daran.

Claude Code


Projekt aufsetzen

Vite ist der Build-Tool der Wahl für Vue 3. Neues Projekt anlegen:

npm create vite@latest meine-ha-app -- --template vue-ts
cd meine-ha-app
npm install

Das erzeugt ein Vue-3-Projekt mit TypeScript. Danach die HA WebSocket-Library installieren:

npm install home-assistant-js-websocket

Zum Entwickeln:

npm run dev

Der Dev-Server läuft unter http://localhost:5173. Noch keine HA-Verbindung, aber die App läuft.


Verbindung zu Home Assistant

Die Verbindung zu HA läuft über home-assistant-js-websocket. Nötig sind URL und ein Access Token.

Access Token anlegen: HA → Profil → Sicherheit → Langlebige Zugriffstoken.

Der elegante Teil: weil die App später als iFrame in HA läuft, ist window.location.origin automatisch die URL des HA-Servers. Kein Config-File, keine Umgebungsvariable.

const HA_URL = window.location.origin

Für das Token empfiehlt sich, es einmalig abzufragen und im localStorage zu speichern — dann ist es nicht im Quellcode und landet nicht versehentlich in einem Repository.

Beim ersten Start prüfen ob ein Token vorhanden ist:

const HA_TOKEN = localStorage.getItem('ha_token') ?? prompt('HA Access Token:') ?? ''
if (HA_TOKEN) localStorage.setItem('ha_token', HA_TOKEN)

Für ein echtes Projekt empfiehlt sich statt prompt() eine eigene Dialog-Komponente — mit Fehlerbehandlung und automatischem Retry bei ungültigem Token. Aber als Einstieg reicht das Prinzip oben.

Verbindung herstellen:

import { createConnection, createLongLivedTokenAuth } from 'home-assistant-js-websocket'

const auth = createLongLivedTokenAuth(HA_URL, HA_TOKEN)
const connection = await createConnection({ auth })

Das war's. Kein kompliziertes Auth-Handling, kein OAuth-Flow.


Ersten Sensor lesen

Mit bestehender Verbindung alle Entities abonnieren:

import { subscribeEntities } from 'home-assistant-js-websocket'

subscribeEntities(connection, (entities) => {
  const temp = entities['sensor.temperatur_wohnzimmer']?.state
  console.log(temp) // z.B. "21.4"
})

Der Callback wird bei jedem State-Update aufgerufen — kein Polling.

Ersten Schalter toggeln

import { callService } from 'home-assistant-js-websocket'

await callService(connection, 'light', 'toggle', { entity_id: 'light.licht_wohnzimmer' })

Das sind die zwei Grundbausteine: Entities abonnieren, Dienste aufrufen. Alles weitere baut darauf auf.


Die App in HA einbinden

Build erzeugen:

npm run build

Den Inhalt des dist/-Ordners auf den HA-Server kopieren, z.B. nach /config/www/meine-app/. In HA ist dieser Ordner dann unter /local/meine-app/ erreichbar.

Im Lovelace-Dashboard einbinden:

type: iframe
url: /local/meine-app/index.html
aspect_ratio: "16:9"

Cache-Busting nicht vergessen: Browser cachen iFrame-Inhalte aggressiv. Nach einem Update sieht man oft noch die alte Version. Lösung: einen Query-Parameter an die URL hängen der bei jedem Deploy geändert wird:

url: /local/meine-app/index.html?v=x7k2q

Das lässt sich im Deploy-Script automatisieren — zufälliger String, per HA-API ins Dashboard-YAML schreiben, Dashboard neu laden.


Wie geht es weiter?

Sobald eine App funktioniert und man merkt dass man mehr braucht — eine zweite App, gemeinsame Logik, Daten die hin- und herfließen — stellt sich die Architektur-Frage. Das ist Thema von Teil 3.


Fazit

  • Claude Code per npm install -g @anthropic-ai/claude-code installieren
  • CLAUDE.md mit Coding-Regeln anlegen — hält den Stil über viele Sessions konsistent
  • Projekt mit npm create vite@latest -- --template vue-ts aufsetzen
  • home-assistant-js-websocket für die Verbindung: window.location.origin als URL, Token im localStorage
  • Entities abonnieren per subscribeEntities, Dienste aufrufen per callService
  • App als statische Dateien nach /config/www/ deployen, per iFrame-Card einbinden
  • Cache-Busting beim Deploy nicht vergessen