Home Assistant Morgenbriefing — TTS med kalender, vejr og elpris

Sådan byggede jeg en daglig kl. 06:00 talebesked i Home Assistant med Google AI TTS (Gemini), calendar.get_events på tværs af tre familiekalendere, EV-ladestatus, Waze-pendlertrafik, live elpris og affaldstæller.

#home-assistant #tts #automation #kalender #google #gemini #elpris
31. maj 2026
Home Assistant Morgenbriefing — TTS med kalender, vejr og elpris

Hver morgen åbnede jeg Home Assistant-appen for at tjekke dagens kalender, kigge på vejret og vurdere om aftenen var et godt tidspunkt at starte opvaskemaskinen baseret på elprisen. Tre separate opslag inden kaffen.

Morgenbriefing-automationen erstattede det hele. Kl. 06:00 læser køkkenhøjttaleren dagens aftaler op på tværs af tre familiekalendere, udetemperaturen, en affaldspåmindelse når afhentning er i morgen og den aktuelle elpris. Det hele tager cirka 15 sekunder.

Hvad briefingen siger

På en typisk morgen lyder det sådan her:

“God morgen! I dag er det mandag. Temperaturen ude er 14 grader. Du har 2 aftaler i dag: tandlæge, teammøde. Husk: affald hentes i morgen. Elprisen er 0,42 kroner per kilowattime.”

På en rolig dag uden aftaler og ingen nært forestående afhentning trimmes den ned til vejr og pris. Intet læses op hvis der ikke er noget at sige.

Nøglemønsteret: calendar.get_events med response_variable

De fleste Home Assistant-automatiseringsguides stopper ved at trigge på en kalenderbegivenhed. Denne automation tager en anden tilgang: den forespørger aktivt kalenderen på dagens begivenheder og bruger svaret i en template.

calendar.get_events-handlingen returnerer et struktureret svar som kan gemmes i en variabel og tilgås i efterfølgende trin:

- action: calendar.get_events
  target:
    entity_id: calendar.rolf_szimnau_dk
  data:
    start_date_time: "{{ today_at('00:00') }}"
    end_date_time: "{{ today_at('23:59') }}"
  response_variable: kalender

Svaret er en dict med entitets-ID som nøgle. Tilgå begivenhedslisten sådan:

{% set events = kalender['calendar.rolf_szimnau_dk']['events'] %}

Hver begivenhed i listen har et summary-felt (begivenhedens titel). Til en talebesked er det det eneste der er brug for.

Tilføjelse af flere kalendere

Kør en separat calendar.get_events-handling for hver kalender med sin egen response_variable, og kombiner derefter listerne i template’en:

- action: calendar.get_events
  target:
    entity_id: calendar.annette_familie
  data:
    start_date_time: "{{ today_at('00:00') }}"
    end_date_time: "{{ today_at('23:59') }}"
  response_variable: kalender_annette

I beskedtemplate’en:

{% set events = events_rolf + events_annette + events_louise %}

Den komplette automation

alias: "Morgenbriefing: TTS kl. 06"
mode: single
trigger:
  - platform: time
    at: "06:00:00"
action:
  - action: calendar.get_events
    target:
      entity_id: calendar.rolf_szimnau_dk
    data:
      start_date_time: "{{ today_at('00:00') }}"
      end_date_time: "{{ today_at('23:59') }}"
    response_variable: kalender
  - action: calendar.get_events
    target:
      entity_id: calendar.annette_familie
    data:
      start_date_time: "{{ today_at('00:00') }}"
      end_date_time: "{{ today_at('23:59') }}"
    response_variable: kalender_annette
  - action: calendar.get_events
    target:
      entity_id: calendar.louise_familie
    data:
      start_date_time: "{{ today_at('00:00') }}"
      end_date_time: "{{ today_at('23:59') }}"
    response_variable: kalender_louise
  - action: tts.speak
    target:
      entity_id: tts.google_ai_tts
    data:
      media_player_entity_id: media_player.hojtaler_kokken
      language: da-DK
      message: >-
        {% set dage = ['mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag', 'søndag'] %}
        {% set dag = dage[now().weekday()] %}
        {% set events_rolf = kalender['calendar.rolf_szimnau_dk']['events'] %}
        {% set events_annette = kalender_annette['calendar.annette_familie']['events'] %}
        {% set events_louise = kalender_louise['calendar.louise_familie']['events'] %}
        {% set events = events_rolf + events_annette + events_louise %}
        {% set temp = state_attr('weather.forecast_home', 'temperature') %}
        {% set affald = states('sensor.affalddk_bytoften_1_naeste_afhentning') | int(99) %}
        {% set elpris = states('sensor.energi_data_service') | float(0) | round(2) %}
        {% set km = states('sensor.skoda_elroq_range') | int(0) %}
        {% set bat = states('sensor.skoda_elroq_battery_percentage') | int(0) %}
        {% set maal = states('sensor.skoda_elroq_target_battery_percentage') | int(80) %}
        {% set lader = states('sensor.skoda_elroq_charging_state') %}
        {% set ac = states('climate.skoda_elroq_air_conditioning') %}
        {% set rejsetid = states('sensor.rejsetid_vejle_travel_time') | float(0) | round(0) | int %}
        God morgen! I dag er det {{ dag }}.
        Temperaturen ude er {{ temp }} grader.
        {% if events | length > 0 %}
        Du har {{ events | length }} aftale{% if events | length > 1 %}r{% endif %} i dag:
        {% for event in events %}{{ event.summary }}{% if not loop.last %}, {% endif %}{% endfor %}.
        {% else %}Ingen aftaler i dag.
        {% endif %}
        {% if affald <= 1 %}Husk: affald hentes i dag!
        {% elif affald <= 2 %}Husk: affald hentes i morgen.
        {% endif %}
        {% if km > 0 and km < 100 %}Elroq har kun {{ km }} kilometer tilbage.
        {% endif %}
        {% if lader == 'charging' %}Elroq lader: {{ bat }}% ud af {{ maal }}%.
        {% elif bat < maal %}Elroq er {{ bat }}% – har ikke nået målet på {{ maal }}%.
        {% endif %}
        {% if ac != 'off' %}Elroq varmer op.
        {% endif %}
        {% if now().weekday() < 5 and rejsetid > 0 %}
        {% if rejsetid > 40 %}Trafik til arbejde: {{ rejsetid }} minutter – forvent forsinkelse.
        {% else %}Kørsel til arbejde: {{ rejsetid }} minutter.
        {% endif %}{% endif %}
        Elprisen er {{ elpris }} kroner per kilowattime.

Tilpasning

Kun hverdage — tilføj en betingelse over handlingerne:

condition:
  - condition: time
    weekday: [mon, tue, wed, thu, fri]

Anden højttaler — erstat media_player.hojtaler_kokken med en vilkårlig HA media player-entitet.

Anden TTS-motor — byt tts.google_ai_tts ud med tts.google_translate_en_com for den gratis mulighed.

Anden elpris-sensor — erstat sensor.energi_data_service med din spot-prissensor.

Tilføj EV-status og forvarmning

To sensorer fra MyŠkoda-integrationen giver briefingen besked om bilen er klar til dagen.

Ladestatus — briefingen taler kun, når der er noget at gøre. Nåede bilen sit mål natten over, siges der ingenting.

{% set bat = states('sensor.skoda_elroq_battery_percentage') | int(0) %}
{% set maal = states('sensor.skoda_elroq_target_battery_percentage') | int(80) %}
{% set lader = states('sensor.skoda_elroq_charging_state') %}
{% if lader == 'charging' %}Elroq lader: {{ bat }}% ud af {{ maal }}%.
{% elif bat < maal %}Elroq er {{ bat }}% – har ikke nået målet på {{ maal }}%.
{% endif %}

sensor.skoda_elroq_target_battery_percentage er sat i MyŠkoda-appen og eksponeres direkte som en HA-sensor. Bruger din EV-integration ikke lademål, erstat maal med et fast tal som 80.

Forvarmningsbekræftelse — hvis en morgenopvarmningsautomation kører få minutter før kl. 06:00, kan briefingen bekræfte at den faktisk startede:

{% set ac = states('climate.skoda_elroq_air_conditioning') %}
{% if ac != 'off' %}Elroq varmer op.
{% endif %}

Det virker fordi opvarmningsautomationen kører kl. 05:55 — fem minutter før briefingen. Kl. 06:00 er climate.skoda_elroq_air_conditioning enten aktiv (én kort bekræftelseslinje) eller stadig off (intet siges — opvarmningsautomationen håndterer selv sin fejlalarm).

Tilføj trafikinformation

Waze Travel Time-integrationen opretter en sensor med aktuel rejsetid inkl. live trafik. Ingen API-nøgle kræves — Waze-data er gratis.

Installer via: Settings → Devices & Services → Tilføj integration → Waze Travel Time

Brug GPS-koordinater (breddegrad,længdegrad-format) til origin og destination fremfor adresser. Waze’s adresse-parsing er upålidelig for danske adresser — koordinater er entydige. Find dine ved at højreklikke på en placering i Google Maps og kopiere koordinatparret.

Sæt Region til EU og Rutetype til hurtigste. Giv sensoren et beskrivende navn, f.eks. Rejsetid Vejle — HA genererer entitets-ID’et ud fra det, typisk sensor.rejsetid_vejle_travel_time.

Tilføj dette blok i beskedtemplate’en, lige før elprisen:

{% set rejsetid = states('sensor.rejsetid_vejle_travel_time') | float(0) | round(0) | int %}
{% if now().weekday() < 5 and rejsetid > 0 %}
{% if rejsetid > 40 %}Trafik til arbejde: {{ rejsetid }} minutter i dag – forvent forsinkelse.
{% else %}Kørsel til arbejde: {{ rejsetid }} minutter.
{% endif %}
{% endif %}

now().weekday() < 5 springer automatisk weekend over. Juster 40-minutters-grænsen til din normale pendlingstid plus en buffer.

Hvorfor tts.speak og ikke den gamle service

Home Assistant 2024+ bruger tts.speak kaldt på TTS-motor-entiteten, med media_player_entity_id som datafelt. Den gamle tts.google_translate_say-service virker stadig men er forældet. Det nye mønster adskiller motoren fra afspilleren — lettere at skifte begge uafhængigt.

tts.google_ai_tts leveres af Google Generative AI-integrationen — den samme integration der driver Gemini-samtaleagenten i Home Assistant. Den kræver en Google AI Studio API-nøgle, men producerer naturlig, menneskelignende tale sammenlignet med den robotagtige lyd fra den gratis Google Translate TTS.