# Vogh Smart Home

- Websted: https://www.vogh.dk/

## Husopgaver som GitHub-issues – fra stemmekommando til ticket

- Link: https://www.vogh.dk/husopgaver-som-github-issues-fra-stemmekommando-til-ticket/
- Udgivet: 2026-10-11T17:50:44+00:00
- Forfatter: ha-upload

“Skuret skal ryddes.” “Pæren i bryggerset er sprunget.” “Husk at skifte filter i
emhætten.” Den slags opgaver plejede at ende på en seddel på køleskabet eller i 
hovedet på én person, hvor de så blev glemt. Nu siger man det højt til Home Assistant,
og et øjeblik efter ligger opgaven som et **issue på GitHub** og venter på at blive
lukket.

Til daglig arbejder jeg med ServiceNow, så en opgave er for mig en ticket. Den har
et nummer, en historik, og nogen lukker den, når den er løst. GitHub Issues giver
præcis det, uden at jeg skal drive et helt ticketsystem derhjemme. Telefon-appen
er god, labels er gratis, og alt er der stadig om tre år.

## Sådan hænger det sammen

 * **Indbakke:** En helt almindelig lokal to-do-liste i Home Assistant, `todo.husopgaver`.
   Den kan alle i huset skrive på via stemmen (“tilføj skift filter til husopgaver”),
   via HA-appen eller via en lille formular på vægtabletten.
 * **Automation:** Hver gang listen ændrer sig, laves alle åbne punkter om til GitHub-
   issues i et privat repository. Bagefter fjernes de fra listen.
 * **Visning:** En template-sensor henter de åbne og de senest lukkede issues hvert
   5. minut, så de kan vises på dashboardet.
 * **Lukning:** Det sker på GitHub, typisk fra telefonen, når opgaven er løst.

To-do-listen er altså _kun_ en indbakke. Opgaverne bor ét sted, nemlig på GitHub,
så der aldrig er to lister, der er uenige om, hvad der mangler.

## Adgang til GitHub

Lav et **fine-grained personal access token** på GitHub, der _kun_ har adgang til
det ene repository og _kun_ har rettigheden “Issues: Read and write”. Slipper tokenet
ud, kan det ikke andet end at rode med husopgaverne. Det lægges i `secrets.yaml`
sammen med ordet Bearer:

    ```nord
    github_husopgaver_token: "Bearer github_pat_xxxxxxxxxxxx"
    ```

## REST-kommandoerne

Det hele ligger i én pakkefil, `packages/husopgaver.yaml`. Først kommandoen, der
opretter et issue:

    ```nord
    rest_command:
      github_create_issue:
        url: https://api.github.com/repos/DIN-BRUGER/husopgaver/issues
        method: POST
        headers:
          Authorization: !secret github_husopgaver_token
          Accept: application/vnd.github+json
          User-Agent: HomeAssistant
        content_type: application/json
        payload: '{{ {"title": title, "body": body} | to_json }}'

      github_list_issues:
        url: https://api.github.com/repos/DIN-BRUGER/husopgaver/issues?state=open&per_page=100
        method: GET
        headers:
          Authorization: !secret github_husopgaver_token
          Accept: application/vnd.github+json
          User-Agent: HomeAssistant
    ```

To detaljer, der er værd at kende:

 * **`| to_json` på hele payloaden.** Så bliver anførselstegn og linjeskift i titlen
   escaped korrekt. Skriver man JSON’en i hånden, går den i stykker første gang 
   nogen skriver _“Ret ‘den’ skuffe”_.
 * **User-Agent.** GitHubs API afviser kald uden en User-Agent. Den kan i øvrigt
   også give problemer den anden vej: Min egen webhost blokerede Home Assistants
   standard-User-Agent, så det er en god vane altid at sætte den selv.

## Automationen: fra liste til issue

    ```nord
    alias: Husopgaver → GitHub issues
    mode: queued
    max: 10
    triggers:
      - trigger: state
        entity_id: todo.husopgaver
    actions:
      - action: todo.get_items
        target:
          entity_id: todo.husopgaver
        data:
          status: needs_action
        response_variable: result
      - variables:
          oprettet_af: >-
            {% set uid = trigger.to_state.context.user_id if trigger.to_state is not none else none %}
            {{ states.person | selectattr('attributes.user_id', 'eq', uid)
               | map(attribute='name') | first
               | default('stemme/ukendt' if uid is none else 'ukendt bruger', true) }}
      - repeat:
          for_each: "{{ result['todo.husopgaver']['items'] }}"
          sequence:
            - action: rest_command.github_create_issue
              data:
                title: "{{ repeat.item.summary }}"
                body: >-
                  {{ repeat.item.description ~ '\n\n' if repeat.item.description is defined
                     and repeat.item.description else '' }}Oprettet af {{ oprettet_af }} via Home Assistant
              response_variable: svar
              continue_on_error: true
            - if:
                - condition: template
                  value_template: "{{ svar is defined and svar.status == 201 }}"
              then:
                - action: todo.remove_item
                  target:
                    entity_id: todo.husopgaver
                  data:
                    item: "{{ repeat.item.uid }}"
                - event: husopgaver_opdater
                  event_data:
                    number: "{{ svar.content.number }}"
    ```

Det vigtigste her er rækkefølgen. **Punktet fjernes først fra listen, når GitHub
har svaret 201 Created.** Er nettet nede, eller udløber tokenet, bliver opgaven 
bare stående på listen og prøves igen ved næste ændring. Intet bliver væk på vejen.
Det er det samme princip som i en god integration på arbejdet: Bekræft modtagelsen,
før du sletter kilden.

Et par andre greb:

 * **Hvem oprettede den?** State-ændringens `context.user_id` slås op i `person`-
   entiteterne. Kommer opgaven fra stemmeassistenten, er der ingen bruger, og så
   står der “stemme/ukendt”. Teksten “Oprettet af …” havner nederst i issuet.
 * **`mode: queued`.** Fjerner automationen et punkt, ændrer listen sig, og automationen
   trigges igen. Med `queued` kører de efter hinanden i stedet for at falde over
   hinanden. Den anden kørsel finder bare en tom liste.
 * **Eventet `husopgaver_opdater`** får sensoren til at hente listen med det samme,
   så det nye issue står på dashboardet med det samme og ikke først om fem minutter.

## Sensoren: issues tilbage på dashboardet

    ```nord
    template:
      - triggers:
          - trigger: homeassistant
            event: start
          - trigger: time_pattern
            minutes: "/5"
          - trigger: event
            event_type: husopgaver_opdater
        actions:
          - action: rest_command.github_list_issues
            response_variable: svar
            continue_on_error: true
        sensor:
          - name: Husopgaver
            unique_id: husopgaver_github_open_issues
            icon: mdi:hammer-wrench
            state: >
              {% if svar is defined and svar.status == 200 %}
                {{ svar.content | rejectattr('pull_request', 'defined') | list | count }}
              {% else %}
                {{ this.state }}
              {% endif %}
            attributes:
              issues: >
                {% if svar is defined and svar.status == 200 %}
                  {% set ns = namespace(l=[]) %}
                  {% for i in svar.content if i.pull_request is not defined %}
                    {% set ns.l = ns.l + [{'number': i.number, 'title': i.title,
                                           'url': i.html_url, 'created': i.created_at,
                                           'labels': i.labels | map(attribute='name') | list}] %}
                  {% endfor %}
                  {{ ns.l }}
                {% else %}
                  {{ this.attributes.issues | default([]) }}
                {% endif %}
    ```

 * **Pull requests filtreres fra.** GitHubs issues-endpoint returnerer også pull
   requests. Dem kan man kende på feltet `pull_request`.
 * **`this.state` som fallback.** Fejler kaldet, beholder sensoren sin sidste værdi
   i stedet for at hoppe til 0 eller “unavailable”. Et kort GitHub-udfald får altså
   ikke det til at se ud, som om alle opgaver er løst.
 * Hos mig henter sensoren også de 10 senest lukkede issues til en liste over klarede
   opgaver, for det er rart at se, hvad der faktisk bliver lavet.

![](https://www.vogh.dk/wp-content/uploads/2026/10/image-9.png)

## Formularen på vægtabletten

Ikke alle vil tale til huset. Derfor er der en lille popup på tabletten med to `
input_text`-felter (titel og beskrivelse) og en knap, der kører et script. Scriptet
lægger opgaven på `todo.husopgaver` og tømmer felterne. Resten klarer automationen.
Det er en pointe i sig selv: Uanset om opgaven kommer fra stemmen, appen eller formularen,
går den _samme_ vej ind. Der er kun én integration at vedligeholde.

## Hvad har jeg lært?

 * **Ét sted for sandheden.** Det er fristende at synkronisere begge veje mellem
   to-do-listen og GitHub, men så skal man håndtere konflikter, sletninger og dubletter.
   Som ren indbakke er listen langt simplere, og der er aldrig tvivl om, hvor opgaven
   bor.
 * **Templates genindlæses ikke altid.** Efter en ændring i template-sensoren skete
   der ingenting, før jeg fyrede eventet manuelt. Tjek altid sensoren bagefter, 
   og genstart HA, hvis den ikke ændrer sig.
 * **Labels er gratis.** Sæt labels på issues på GitHub, så kommer de automatisk
   med i sensorens attributter og kan bruges til at filtrere eller farve listen 
   på dashboardet. Det kræver ingen ekstra kode i Home Assistant.

Vil du se resten af huset? [Her er oversigten over mit Home Assistant-setup](https://www.vogh.dk/mit-home-assistant-setup-et-hus-der-naesten-passer-sig-selv/).

## Škodas officielle API i Home Assistant – hvad det kan, og hvad det ikke kan

- Link: https://www.vogh.dk/skodas-officielle-api-i-home-assistant-hvad-det-kan-og-hvad-det-ikke-kan/
- Udgivet: 2026-10-10T20:41:29+00:00
- Forfatter: ha-upload

Škoda har lanceret et **officielt, offentligt API** til MyŠkoda: Man laver en API-
nøgle, vælger sin bil, og så kan man hente bilens data med et helt almindeligt REST-
kald. Indtil nu har vores Elroq været koblet på Home Assistant med HACS-integrationen
MySkoda, der bruger den samme bagvej som app’en. Jeg har flyttet så meget som muligt
over på det officielle API – og undervejs lært, hvor grænserne går.

## Det korte svar

| Behov | Officielt API | 
| Batteri, rækkevidde, kilometertæller | ⌊✅⌉ læses | 
| Ladekabel, ladetilstand, ladeeffekt, resttid, ladegrænse | ⌊✅⌉ læses | 
| Døre låst, vinduer, motorhjelm, klima | ⌊✅⌉ læses | 
| Parkeringsposition og ladeprofiler | ⌊✅⌉ læses | 
| Start/stop opladning og klima, ladegrænse | ⌊✅⌉ kommandoer | 
| **Lås døre op/i** | ⌊❌⌉ findes ikke | 
| Sædevarme, “vågn op”, udetemperatur, billeder af bilen | ⌊❌⌉ findes ikke | 
| Lås stik op efter opladning | ⌊⚠⌉ kun ved at overskrive hele ladeprofilen |

Den store mangel er **oplåsning**. Vi har en automation, der låser bilen op hverdagsmorgener,
når den har ladet – den kan ikke flyttes. Så MySkoda bliver ved med at køre side
om side med API’et, men kun til de ting, API’et ikke kan.

## Grænsen: 20 kald i timen

API’et tillader **20 kald i timen pr. bil**, og fejlede kald tæller med. Det former
hele opsætningen: Bilens data hentes med _ét_ kald hvert 5. minut (12 i timen), 
og alle sensorer læser fra det samme svar. Det giver luft til et par kommandoer –
men også en lille forsinkelse: Sætter man ladekablet i, går der op til 5 minutter,
før Home Assistant ved det.

Tip: Hver genindlæsning af REST-integrationen henter også data. Retter man filen
mange gange i samme time, kan man løbe ind i fejl 429.

## Opsætningen

Alt ligger i én pakkefil med den indbyggede `rest`-integration – ingen HACS. API-
nøglen ligger i `secrets.yaml`. Her er et uddrag med et par af sensorerne:

    ```nord
    rest:
      - resource: "https://public.api.connect.skoda-auto.cz/api/v1/vehicles/DIT_VIN"
        scan_interval: 300          # 12 af de 20 kald i timen
        timeout: 20
        headers:
          X-API-Key: !secret skoda_public_api_key
        sensor:
          - name: "Škoda Elroq API Batteri"
            unique_id: skoda_api_battery_soc
            unit_of_measurement: "%"
            device_class: battery
            state_class: measurement
            availability: >-
              {{ value_json is mapping
                 and value_json.get('vehicle', {}).get('charging', {}).get('status', {})
                      .get('battery', {}).get('stateOfChargeInPercent') is number }}
            value_template: "{{ value_json.vehicle.charging.status.battery.stateOfChargeInPercent }}"

          # Ladetilstand på dansk – ukendte værdier vises som API'ets egen tekst
          - name: "Škoda Elroq API Ladetilstand"
            unique_id: skoda_api_charging_state
            value_template: >-
              {%- set raa = value_json.vehicle.charging.status.state | upper -%}
              {{ {'CHARGING': 'Lader', 'CONNECT_CABLE': 'Sæt kablet i',
                  'READY_FOR_CHARGING': 'Klar til opladning',
                  'CONSERVING': 'Opladet – holder niveau'}.get(raa, raa) }}

          # Position til kortkort: latitude/longitude som attributter
          - name: "Škoda Elroq API Position"
            unique_id: skoda_api_position
            value_template: >-
              {{ value_json.vehicle.chargingProfiles.currentVehiclePositionProfile.name
                 if value_json.vehicle.get('chargingProfiles', {}).get('currentVehiclePositionProfile') is mapping
                 else 'Ude' }}
            json_attributes_path: "$.vehicle.parkingPosition.gpsCoordinates"
            json_attributes:
              - latitude
              - longitude

        binary_sensor:
          - name: "Škoda Elroq API Ladekabel"
            unique_id: skoda_api_plug_connected
            device_class: plug
            value_template: >-
              {{ value_json.vehicle.charging.status.plugConnectionState | upper == 'CONNECTED' }}
    ```

I alt laver det ene kald 20 sensorer: batteri, rækkevidde, kilometertæller, ladetilstand,
ladeeffekt, resttid, ladegrænse, klima, sidst opdateret, position, låse- og dørstatus,
ladeprofil, ladevindue og nummerplade.

### Et smart trick: “hjemme” uden device tracker

API’et har ingen “home/not_home”, men det fortæller, hvilken **ladeprofil** der 
gælder på bilens nuværende position. Vores profil hedder “Hjem”, så positionssensoren
viser “Hjem”, når bilen holder i indkørslen, og “Ude” ellers. Det er alt, lade-påmindelsen
behøver.

## Tre ting jeg lærte på den hårde måde

 1. **En binær REST-sensor må ikke have attributter.** Jeg gav “døre låst” et par attributter,
    og så afviste Home Assistant _hele_ REST-blokken – ingen af de nye sensorer blev
    oprettet. Fejlen står i loggen, ikke i brugerfladen. Attributterne endte på en 
    almindelig sensor i stedet.
 2. **Læs specifikationen, ikke kun dokumentationen.** Dokumentationssiden nævner kun
    ét endpoint, men den fulde OpenAPI-specifikation ligger offentligt og viser alle
    felter og alle 12 endpoints. Det var her, det stod klart, at der ingen låsekommando
    er.
 3. **Sammenlign med det gamle, før du skifter.** Før dashboards og automationer blev
    flyttet, tjekkede jeg, at batteri, rækkevidde, kilometertal, ladegrænse og alle
    dør-/vinduesstatusser var identiske mellem API’et og MySkoda.

## Skiftet

Bilen optræder på fem dashboards. Her blev 13 MySkoda-sensorer udskiftet med API-
sensorerne – også inde i kortenes skabeloner, f.eks. ikonet for ladestikket. Det,
API’et ikke kan, peger stadig på MySkoda: knapperne, udetemperaturen og billederne
af bilen. Lade-påmindelsen kl. 19:15 kører nu udelukkende på API’et.

## Er det værd at skifte?

Til at **læse** data: ja. Det er officielt, stabilt og dokumenteret, og det kører
med Home Assistants egen REST-integration. Til at **styre** bilen er det endnu for
begrænset – så længe der ikke er en låsekommando, kan man ikke helt undvære den 
uofficielle integration. Jeg håber, Škoda udvider API’et; indtil da kører de to 
side om side.

![](https://www.vogh.dk/wp-content/uploads/2026/10/image-7.png)

## Mine HACS-favoritter – de pakker huset ikke kunne undvære

- Link: https://www.vogh.dk/mine-hacs-favoritter-de-pakker-huset-ikke-kunne-undvaere/
- Udgivet: 2026-10-10T20:36:56+00:00
- Forfatter: ha-upload

**HACS** (Home Assistant Community Store) er stedet, hvor fællesskabet deler integrationer,
kort og temaer, der ikke er med i Home Assistant fra start. Jeg har 58 pakker installeret–
men nogle af dem er så centrale, at store dele af huset ville holde op med at virke
uden dem. Her er favoritterne, og hvad de bruges til i praksis.

## Integrationer der forbinder huset

### IHC (dingusdk) + IHC Viewer

Den faste el-installation er LK IHC, og beta-udgaven af IHC-integrationen læser 
hele projektet fra controlleren og laver 42 entiteter helt automatisk. IHC Viewer
viser projektet i et panel med live-status, så man kan finde det rigtige ID. [Læs mere i IHC-indlægget](https://www.vogh.dk/lk-ihc-i-home-assistant-naar-den-faste-installation-skal-vaere-smart/).

### Fresh Intellivent Sky (BLE)

Kobler badeværelsets udsugning på via Bluetooth. Uden den kunne Home Assistant ikke
styre udsugningen efter en ekstern fugtsensor – og udsugningens egen sensor var 
ubrugelig. [Læs mere om fugtstyringen](https://www.vogh.dk/fugtstyret-udsugning-naar-den-indbyggede-sensor-ikke-kan-finde-ud-af-det/).

### LocalThings

Styrer Samsung-hvidevarerne **lokalt** på netværket – helt uden Samsung-konto og
sky. Det er den, der sender besked, når opvasker, vaskemaskine og tørretumbler er
færdige.

### LLM Vision

Sender kamerabilleder til en AI-model og giver svaret tilbage til en automation.
Hos os tæller den affaldsbeholderne ved garagen, så stemmebeskeden om affald springes
over, når en spand allerede er kørt ud. Lærdommen: Spørg kun om det, kameraet kan
se. [Læs mere i affaldsindlægget](https://www.vogh.dk/affald-fra-mail-til-stemmebesked-saadan-husker-vi-skraldespanden/).

### Monta og Mammotion

Ladeboksen og robotplæneklipperen. Monta viser og styrer opladningen af elbilen,
og Mammotion lader plæneklipperen overvåges og styres fra Home Assistant.

### Variable

En lille, upåagtet pakke, der giver entiteter, man kan skrive vilkårlige attributter
til. Mealie-automationerne gemmer dagens og morgendagens måltider her – én attribut
pr. måltidstype – så dashboardet kan vise dem. [Læs mere i Mealie-indlægget](https://www.vogh.dk/mealie-i-home-assistant-og-fire-fejl-der-aldrig-viste-sig/).

### Spook

En værktøjskasse med ekstra handlinger og – det bedste – advarsler om ting, der 
peger på entiteter, som ikke findes længere. Den fanger de døde referencer, der 
ellers ville give stille fejl.

## Kort til dashboards

### Bubble Card

Den absolutte favorit. Bubble Card bruges overalt på væg-tabletten og mobilen: store
knapper, pop-ups for hvert rum og moduler fra Module Store. På pizza-fanen viser
_timer\_progress_-modulet en fremdriftsbjælke i hver knap. [Se pizza-indlægget](https://www.vogh.dk/pizzadej-med-72-timers-koldhaevning-styret-af-home-assistant/).

**Fælde:** _timer\_progress_-modulet viser en usynlig bjælke, hvis farven er en 
almindelig hex-kode som `#f4b942`. Brug i stedet `rgba(244, 185, 66, 1)` eller et
farvenavn fra temaet. Pas på – farvevælgeren i Bubble-editoren gemmer hex, så den
kan bringe fejlen tilbage.

### Mushroom og card-mod

Mushroom giver pæne, ensartede kort med minimal opsætning, og card-mod lader mig
finjustere med CSS, hvor de indbyggede muligheder ikke rækker. card-mod skal bruges
med omtanke: Den piller ved brugerfladens indre, og det kan gå i stykker efter en
opdatering.

### apexcharts-card og mini-graph-card

Grafer: mini-graph-card til små, lette grafer på oversigterne og apexcharts-card,
når der skal mere til – f.eks. spotpriser og energi.

## Til nørderne

 * **NeonPlan 3D** – en 3D-plantegning af stueetagen med lys og sensorer live.
 * **Voice Satellite** – gør væg-tabletten til en stemmeassistent.
 * **Device Pulse** – holder øje med, om netværksenhederne svarer, med tabel og 
   tidslinje.
 * **WebRTC Camera** – kamerastreams næsten uden forsinkelse.

## Gode råd om HACS

 * **Tjek, om pakken stadig vedligeholdes.** Se på, hvornår den sidst fik en ny 
   version. Mange ældre kort er erstattet af ting, der nu er indbygget.
 * **Foretræk indbygget, når det findes.** Hver HACS-pakke er en afhængighed mere,
   der skal følge med opdateringerne.
 * **Læs udgivelsesnoterne, før du opdaterer.** Især integrationer, der taler med
   hardware, kan ændre sig.

## Pizzadej med 72 timers koldhævning – styret af Home Assistant

- Link: https://www.vogh.dk/pizzadej-med-72-timers-koldhaevning-styret-af-home-assistant/
- Udgivet: 2026-10-10T20:15:52+00:00
- Forfatter: ha-upload

Pizzaaften er en institution hos os, og dejen er det hele værd. Den laves med meget
lidt gær og hæver langsomt i køleskabet i tre døgn. Det giver en luftig, sprød bund
med masser af smag. Det svære er ikke opskriften, men at huske, hvornår man er nået
til hvilket trin. Derfor har Home Assistant fået en fane med pizza-timere, og køkkenlampen
fortæller, hvornår det er tid til næste trin.

Vi har været på kursus hos [Pizza jon](https://jonsmadklub.dk/) og derfor går vi
op i hvordan en Napolitansk Pizza skal laves.

![](https://www.vogh.dk/wp-content/uploads/2026/10/IMG_3666-rotated-1-edited-704x1024.
jpg)

## Opskriften

| Ingrediens | Mængde | Bagerprocent | 
| Mel Caputo Cuoco | 900 g | 100 % | 
| Vand | 622 g | ca. 69 % | 
| Salt | 24 g | ca. 2,7 % | 
| Gær | 1,5 g | ca. 0,17 % |

Det giver godt 1,5 kg dej. Den meget lille mængde gær er pointen: Dejen skal ikke
hæve hurtigt, den skal udvikle smag over tre døgn i køleskabet.

 1. Rør vand og gær sammen.
 2. **Timer 1 – 12 min:** Tilsæt melet lidt ad gangen, og ælt.
 3. **Timer 2 – 5 min:** Tilsæt saltet, og ælt videre.
 4. Tag dejen ud på bordet, og stram den op.
 5. **Timer 3 – 20 min:** Lad dejen hvile på køkkenbordet.
 6. **Timer 4 – 72 timer:** Dejen i køleskabet.
 7. Tag dejen ud **5 timer før brug**, del den i pizzaboller, og lad dem hæve i en 
    varm hævekasse.

[”Macte” Voyager Twin Elektrisk Pizzaovn](https://jonsmadklub.dk/products/macte-ovens-voyager-twin-pizzaovn?srsltid=AU7gw4WsVKOf_91GjW0GnC0An10ykIfH6qa8DgnPp3uO5m1V7a_qUuxD)

## Pizza-fanen i Home Assistant

På mobilen har vi en fane, “Pizza ⌊🍕⌉”, med opskriften øverst og en stor knap for
hvert trin. Knapperne er lavet med **Bubble Card**, og hver knap har en fremdriftsbjælke,
der viser, hvor langt timeren er nået. Så kan man se det fra den anden ende af køkkenet
med mel på fingrene.

![](https://www.vogh.dk/wp-content/uploads/2026/10/image-6.png)

## Køkkenlampen som signal

Det bedste ved det hele er lampen. Én af Hue-spotsene over køkkenbordet bliver **
grøn**, når et trin starter, og **rød**, når timeren er færdig. Rød betyder “næste
trin” – tilsæt salt, tag dejen ud, sæt den i køleskabet. Man behøver ikke kigge 
på telefonen.

Spotsene får strøm fra en IHC-udgang, så automationen tænder altid IHC-udgangen 
først. Ellers ville Hue-spotten ikke have strøm til at skifte farve. Timer 4 (køleskabet)
rører ikke lampen – den skal ikke lyse grønt i tre døgn.

    ```nord
    # Uddrag – samme mønster for trin 1, 2 og 3
    triggers:
      - trigger: state
        entity_id: input_button.pizza_1
        id: pizza1
      - trigger: state
        entity_id: [timer.pizza_1, timer.pizza_2, timer.pizza_3]
        to: idle
        id: faerdig
    actions:
      - if:
          - condition: trigger
            id: pizza1
        then:
          # IHC-udgangen giver strøm til Hue-spotsene
          - action: light.turn_on
            target:
              entity_id: light.kokken_ihc
          - action: light.turn_on
            target:
              entity_id: light.hue_spot_koekken
            data:
              rgb_color: [42, 242, 2]      # grøn: i gang
          - action: timer.start
            target:
              entity_id: timer.pizza_1
      - if:
          - condition: trigger
            id: faerdig
        then:
          - action: light.turn_on
            target:
              entity_id: light.hue_spot_koekken
            data:
              rgb_color: [255, 0, 0]       # rød: næste trin
    ```

En **Reset**-knap stopper alle timere og sætter lampen tilbage til almindeligt hvidt
lys.

## En fejl jeg fandt undervejs

Da jeg skrev dette indlæg, kiggede jeg på timernes indstillinger. Alle fire havde**“
Gendan” slået fra**. For en timer på 12 minutter er det ligegyldigt, men timer 4
kører i 72 timer, og hvis Home Assistant genstarter i den periode – efter en opdatering
f.eks. – bliver timeren nulstillet. Dejen er stadig i køleskabet, men nedtællingen
er væk. Nu er “Gendan” slået til, så timerne fortsætter efter en genstart.

Lærdommen gælder alle lange timere: **Kører en timer i timer eller dage, så slå “
Gendan” til.** Indstillingen er slået fra som standard.

## LK IHC i Home Assistant – når den faste installation skal være smart

- Link: https://www.vogh.dk/lk-ihc-i-home-assistant-naar-den-faste-installation-skal-vaere-smart/
- Udgivet: 2026-10-10T19:34:57+00:00
- Forfatter: ha-upload

Huset har **LK IHC** som fast el-installation: Alle tryk, spots og loftudtag er 
trukket til en central controller i stedet for at sidde direkte på hver sin kontakt.
Det er gammeldags “smart home” i ordets bedste betydning – robust, uden batterier
og uden sky. Men det bliver først rigtig sjovt, når det kan tale sammen med resten
af huset. Her er, hvordan IHC er koblet på Home Assistant, og hvilke faldgruber 
jeg er gået i.

## Integrationen

Home Assistant har en indbygget IHC-integration, men jeg bruger **beta-udgaven fra
dingusdk**, som installeres via HACS. Det er den samme integration, bare med nyere
ændringer og opsætning direkte i brugerfladen. Controlleren står på netværket, og
integrationen logger på den.

Det smarte er **auto-opsætningen**: Integrationen læser IHC-projektet fra controlleren
og opretter selv en entitet for hver udgang. Hos os giver det **42 entiteter** –
41 lys og én kontakt. Hvert lys får attributter direkte fra projektet:

| Attribut | Eksempel | 
| `ihc_id` | 1515355 | 
| `ihc_name` | Lampeudtag | 
| `ihc_position` | I loft | 
| `ihc_note` | Lampe udtag Ø80mm i loft | 
| Navn i HA | Lampeudtag (I loft) Alrum (StuePlan)_1515355 |

Ved siden af kører **IHC Viewer** (også fra dingusdk), som viser hele IHC-projektet
i et panel i Home Assistant med live-status. Den er guld værd, når man skal finde
ud af, hvilket ID der hører til hvilket tryk.

## Faldgrube 1: Navnene lyver

Auto-opsætningen er kun så god som projektet i controlleren – og projektet blev 
lavet af elektrikeren, da huset blev bygget. Det passer ikke altid med virkeligheden:

 * **Badeværelset på 1. sal**: Spotsene i loftet sidder på en udgang, der i projektet
   hedder “Lampeudtag (I væg) – lampeudtag v. spejle”. Udgangen, der hedder “I loft”,
   er ikke forbundet til noget. Automationer og dashboards var bygget på den forkerte–
   så tabletten viste lyset som slukket, mens det var tændt.
 * **Rumnavnene** følger elektrikerens tegning, ikke den måde vi bruger huset på.
   En udgang i “Hall/Gang” forsyner f.eks. spotsene i køkkengangen.

Lærdommen: **Tænd og sluk hver udgang og se, hvad der sker**, før du bygger noget
på den. Behold entitets-id’et (det indeholder IHC-id’et og er nyttigt), men giv 
entiteterne et visningsnavn, der passer til virkeligheden.

## Faldgrube 2: IHC og Hue i samme lampe

Flere steder sidder der **Philips Hue-spots i IHC-udgange**. IHC-udgangen er strømmen,
og Hue er farven og lysstyrken. Det giver det bedste fra begge verdener – vægtrykket
virker altid – men også en fælde: Når IHC-udgangen er slukket, har Hue-pærerne ingen
strøm. Nogle Hue-grupper bliver så `unavailable`, andre bliver ved med at påstå,
at de er tændt, selvom der er mørkt.

Løsningen på dashboardet er et fast mønster for alle de lamper: **Tryk** tænder 
og slukker IHC-udgangen (strømmen), og **hold** åbner Hue-gruppen, hvor man kan 
skrue på farve og lysstyrke. Så er der aldrig tvivl om, hvad knappen gør, og man
kan ikke “tænde” en Hue-pære uden strøm.

![](https://www.vogh.dk/wp-content/uploads/2026/10/image-5.png)

## Vagthunden

Forbindelsen til controlleren kan falde ud, og så bliver alle IHC-entiteter `unavailable`.
Integrationen genopretter ikke altid selv forbindelsen, men en genindlæsning af 
integrationen gør. Derfor har jeg en lille vagthund: Bliver et kendt IHC-lys utilgængeligt
i mere end fem sekunder, genindlæses integrationen. Om dagen siger Home Assistant
Voice det højt, om natten kommer det som en push-besked.

    ```nord
    alias: IHC reboot
    mode: single
    triggers:
      - trigger: state
        entity_id: light.kontor_stueplan_20059   # et vilkårligt IHC-lys
        to: unavailable
        for:
          seconds: 5
    actions:
      - if:
          - condition: sun
            after: sunrise
            before: sunset
        then:
          - action: tts.speak
            target:
              entity_id: tts.piper
            data:
              media_player_entity_id: media_player.home_assistant_voice
              message: IHC er nede - genstarter servicen
        else:
          - action: notify.mobile_app_din_telefon
            data:
              title: IHC er nede
              message: Genstarter IHC-integrationen
      - action: homeassistant.reload_config_entry
        data:
          entry_id: "{{ config_entry_id('light.kontor_stueplan_20059') }}"
    ```

`config_entry_id()` finder integrationens id ud fra lyset, så der ikke skal stå 
et hårdkodet id i automationen.

## Hvad IHC bruges til

 * **Morgenlys på badet**, der selv slukker efter 30 minutter.
 * **Aftenlys** efter solnedgang og “sluk alt”, når ingen er hjemme.
 * **Stemmestyring** via Home Assistant Voice – “tænd i køkkenet” virker, selvom
   lyset sidder på IHC.
 * **En plantegning** på tabletten, hvor man trykker på rummet for at tænde lyset.

## Det jeg har lært

 * **Auto-opsætning er et udgangspunkt, ikke en sandhed.** Navnene kommer fra et
   projekt, der er lavet for mange år siden af en anden.
 * **Hold styr på hvem der har strømmen.** Når en smart pære sidder i en styret 
   udgang, skal brugerfladen afspejle det.
 * **Byg en vagthund til det, der kan falde ud.** Hellere en automatisk genindlæsning
   end et mørkt hus, der ikke reagerer.

## Mealie i Home Assistant – og fire fejl der aldrig viste sig

- Link: https://www.vogh.dk/mealie-i-home-assistant-og-fire-fejl-der-aldrig-viste-sig/
- Udgivet: 2026-10-10T19:30:43+00:00
- Forfatter: ha-upload

**Mealie** er en selvhostet opskriftssamling med madplan og indkøbsliste. Hos os
kører den som App direkte på Home Assistant, og den officielle Mealie-integration
binder den sammen med resten af huset. Idéen er enkel: Planlæg ugens mad i Mealie–
og så dukker den op i familiens kalender og på mobilen, uden at nogen skal skrive
den to steder.

Da jeg skulle skrive dette indlæg, opdagede jeg dog, at den del, der flytter madplanen
over i kalenderen, havde **fire fejl** – som aldrig var blevet opdaget. Mere om 
det nedenfor.

## Hvad integrationen giver

 * **En kalender pr. måltidstype** – morgenmad, frokost, aftensmad, snack, tilbehør,
   dessert og drikke – med det, der er planlagt i Mealie.
 * **Indkøbslisten som to-do-liste** i Home Assistant, så den kan bruges fra mobilen
   og med stemmen.
 * **Handlinger** til at hente, oprette, ændre og slette madplaner, søge i opskrifter
   og importere en opskrift fra en URL.

## Sådan hænger det sammen

 1. **Dagens og morgendagens måltider**: To automationer kører hver time og ved opstart.
    De henter dagens og morgendagens begivenheder fra Mealie-kalenderne og gemmer dem
    som attributter på en variabel (via HACS-integrationen _Variable_) – én attribut
    pr. måltidstype.
 2. **Mobil-dashboardet** viser to markdown-kort, “Dagens måltider” og “Morgendagens
    måltider”, som løber variablens attributter igennem og kun viser dem, der har noget
    planlagt.
 3. **Familiekalenderen**: Hver nat kl. 00:05 kopierer en automation de næste syv dages
    madplan over i kalenderen “Madplan”, som hele familien har på telefonen.

![](https://www.vogh.dk/wp-content/uploads/2026/10/image-4.png)

## Fire fejl der aldrig viste sig

Synkroniseringen til familiekalenderen så fornuftig ud og kørte hver nat uden fejl.
Men den havde aldrig flyttet en eneste ret – og grunden til, at ingen opdagede det,
var lige så simpel: Madplanen i Mealie havde været tom i flere måneder. En løkke
over en tom liste fejler ikke. Den gør bare ingenting.

Så snart der kom en ret i planen, ville den være gået galt på fire måder:

 1. **Forkerte feltnavne.** Automationen læste `meal_type` og `date`, men Mealie-integrationen
    sender `entry_type` og `mealplan_date`. Datoen ville være tom.
 2. **Datoen er et dato-objekt.** `mealplan_date` er ikke tekst, men et Python-dato-
    objekt. Det får `repeat: for_each` til at afvise hele listen med “must be a list
    of items”. Løsningen er først at lave listen om til simple tekstværdier.
 3. **Ingen dubletkontrol.** Hver nat blev de næste syv dage oprettet igen. En ret 
    planlagt en uge frem ville altså ende med at stå i kalenderen op til syv gange.
    Nu slår automationen først op i kalenderen og springer over, hvis samme dato og
    titel allerede står der.
 4. **Slutdato = startdato.** En heldagsbegivenheds slutdato er _eksklusiv_ – den skal
    være dagen efter. Med samme dato som start har begivenheden ingen længde.

## Sådan testede jeg det

Når planen er tom, kan man ikke se, om noget virker. Integrationen kan heldigvis
både oprette og slette måltider, så testen kan ryddes helt op bagefter:

 1. `mealie.set_mealplan` med en note som titel – “TEST fra Home Assistant” – til aftensmad
    i morgen. Svaret viste de rigtige feltnavne.
 2. Kør synkroniseringen: én heldagsbegivenhed i morgen, med den rigtige slutdato.
 3. Kør den igen: ingen ny begivenhed – dubletkontrollen virker.
 4. Kør “Hent Mealie i morgen”: testretten står under aftensmad.
 5. `mealie.delete_mealplan` med id’et fra trin 1: planen er tom igen.

## Automationen

    ```nord
    alias: Sync Mealie måltider til kalender
    mode: single
    variables:
      meal_icons:
        breakfast: 🌅
        lunch: ☀
        dinner: 🌙
        snack: 🍎
        side: 🥗
        dessert: 🍰
        drink: 🥤
    triggers:
      - trigger: time
        at: "00:05:00"
    actions:
      - action: mealie.get_mealplan
        data:
          config_entry_id: DIN_MEALIE_CONFIG_ENTRY
          start_date: "{{ now().strftime('%Y-%m-%d') }}"
          end_date: "{{ (now() + timedelta(days=7)).strftime('%Y-%m-%d') }}"
        response_variable: plan
      # mealplan_date er et dato-objekt – lav listen om til tekst først
      - variables:
          maaltider: >-
            {% set ns = namespace(l=[]) %}
            {% for m in plan.mealplan %}
              {% set type = (m.entry_type | string | lower).split('.')[-1] %}
              {% set ns.l = ns.l + [{
                   'dato': m.mealplan_date | string,
                   'type': type,
                   'titel': meal_icons.get(type, '🍽') ~ ' ' ~ (m.recipe.name if m.recipe else m.title)}] %}
            {% endfor %}
            {{ ns.l }}
      - action: calendar.get_events
        target:
          entity_id: calendar.madplan
        data:
          start_date_time: "{{ now().strftime('%Y-%m-%d') }} 00:00:00"
          end_date_time: "{{ (now() + timedelta(days=8)).strftime('%Y-%m-%d') }} 23:59:59"
        response_variable: eksisterende
      # 'dato|titel' for alt, der allerede står i kalenderen
      - variables:
          kendte: >-
            {% set ns = namespace(l=[]) %}
            {% for e in eksisterende['calendar.madplan'].events %}
              {% set ns.l = ns.l + [(e.start | string)[:10] ~ '|' ~ e.summary] %}
            {% endfor %}
            {{ ns.l }}
      - repeat:
          for_each: "{{ maaltider }}"
          sequence:
            - if:
                - condition: template
                  value_template: "{{ (repeat.item.dato ~ '|' ~ repeat.item.titel) not in kendte }}"
              then:
                - action: calendar.create_event
                  target:
                    entity_id: calendar.madplan
                  data:
                    summary: "{{ repeat.item.titel }}"
                    start_date: "{{ repeat.item.dato }}"
                    # heldagsbegivenhed: slutdatoen er eksklusiv
                    end_date: "{{ (strptime(repeat.item.dato, '%Y-%m-%d') + timedelta(days=1)).strftime('%Y-%m-%d') }}"
                    description: "Fra Mealie ({{ repeat.item.type }})"
    ```

## Det jeg har lært

 * **En tom liste skjuler alt.** En automation, der løber over en liste, kan være
   fuld af fejl og stadig køre “grønt” hver nat, så længe listen er tom.
 * **Kig på det rigtige svar.** Feltnavnene står i handlingens svar – kør den én
   gang i Udviklerværktøjer og se, hvad der faktisk kommer tilbage, før du skriver
   skabelonerne.
 * **Alt, der kører igen og igen, skal kunne køre igen.** En natlig synkronisering
   skal tjekke, hvad der allerede findes – ellers bygger den dubletter op.
 * **Test med data, du kan fjerne igen.** Opret en testpost, kør, verificér og slet–
   så er testen gentagelig og efterlader intet.

## Affald fra mail til stemmebesked – sådan husker vi skraldespanden

- Link: https://www.vogh.dk/affald-fra-mail-til-stemmebesked-saadan-husker-vi-skraldespanden/
- Udgivet: 2026-10-09T20:37:32+00:00
- Forfatter: ha-upload

Hvem har ikke stået en morgen og hørt skraldebilen dreje om hjørnet – med spanden
stadig inde i carporten? Hos os er det et løst problem. Forsyningen sender en mail
dagen før tømning, og Home Assistant gør resten: push-besked, kalender, kort på 
tabletten, en stemmebesked om aftenen og til sidst et kamera, der tjekker, om spanden
faktisk står ude.

## Overblikket

 1. Ca. kl. 16 dagen før tømning kommer en mail fra forsyningen (via affaldonline.dk).
 2. Home Assistant læser mailen og sender en push-besked, gemmer teksten og sikrer,
    at tømningen står i affaldskalenderen.
 3. Tabletten i køkkenet viser et kort: “Mad/Rest – tømmes i morgen”.
 4. Kl. 19 siger Home Assistant Voice beskeden højt – medmindre nogen allerede har 
    trykket “Kørt ud” på tabletten.
 5. Kl. 18 aftenen før – og når der sker noget ved spanden – tæller et kamera med AI
    beholderne på deres faste plads. Mangler der en, er den kørt ud, og stemmebeskeden
    kl. 19 springes over.

## 1. En mailkonto kun til Home Assistant

Mailen fra forsyningen går til en separat mailkonto, som kun Home Assistant læser.
Den er koblet på med den indbyggede **IMAP-integration**, som affyrer et `imap_content`-
event, hver gang der kommer en ny mail. Kontoen er kun til notifikationer – den 
kan ikke bruges til at styre noget.

Det lyder enkelt, men der var en del fælder undervejs:

 * **Afsenderen er ikke forsyningen.** Mailen kommer fra `noreply@affaldonline.dk`,
   mens Return-Path er `no_reply@affaldonline.dk`. Derfor matcher automationen på“
   affaldonline” som en del af afsenderen i stedet for en præcis adresse.
 * **Emnefeltet kommer forkert ud.** “Tømning” bliver til “T?mning”, mens selve 
   mailteksten har korrekte danske tegn. Al tekst hentes derfor fra mailens indhold.
 * **Teksten er HTML.** Feltet `text` indeholder HTML-udgaven, ikke den rene tekst.
   Fjerner man bare HTML-tags, står hele CSS-blokken tilbage – så `<style>` skal
   fjernes først.
 * **Standardgrænsen på 2048 tegn er for lille.** Mailen bliver klippet af, før 
   den egentlige besked kommer. Sæt _max message size_ i integrationens indstillinger
   op – her er den 16384.
 * **Datoen er ikke en tekst.** `trigger.event.data.date` er et datetime-objekt.
   Prøver man at lave hele eventet om til JSON, fejler automationen stille: Den 
   står som kørt, men der sker ingenting.
 * **Marker mailen som læst.** Ingen læser kontoen, så uden et kald til `imap.seen`
   bliver mailen ved med at være ulæst – og hver genstart af Home Assistant giver
   en ny notifikation.

### Fra HTML-mail til en sætning man kan sige højt

Mailen ser sådan ud: _“Du får tømt din mad- og restaffaldsbeholder – [adresse] –
i morgen.”_ Automationen renser den i tre trin med variabler:

    ```nord
    variables:
      # 1. Fjern <style>, så HTML-tags, og skær hilsenen fra
      body_clean: >-
        {% set c = trigger.event.data.text | default('') | string
           | regex_replace('(?s)<style.*?</style>', ' ')
           | regex_replace('<[^>]+>', ' ')
           | replace(' ', ' ')
           | regex_replace('\s+', ' ') | trim %}
        {{ c.split('Mvh.')[0] | trim }}
      # 2. Brug en standardtekst, hvis der ikke kom noget brugbart ud
      msg: "{{ body_clean if body_clean | length > 5 else 'Ny besked om affaldstømning' }}"
      # 3. Fjern adressen (det midterste led) til stemmebeskeden
      spoken: >-
        {% set p = msg.split(' - ') %}{% if p | length >= 3 %}{{ (p[0] ~ ' ' ~ (p[2:] | join(' '))) | regex_replace('\s+', ' ') | trim }}{% else %}{{ msg }}{% endif %}
    ```

Push-beskeden får hele teksten, men stemme beskeden får udgaven uden adresse. Det
lyder meget mere naturligt at høre _“Du får tømt din mad- og restaffaldsbeholder
i morgen”_ end at få læst sin egen adresse op. Teksten gemmes i en `input_text` (
klippet til 250 tegn, da en `input_text` højst kan rumme 255), og en `input_boolean`
slås til som flag for, at der venter en besked.

## 2. Kalenderen er sandheden

Affaldskalenderen fyldes normalt udefra med alle årets tømninger. Mail-automationen
bruger den som sikkerhedsnet: Hvis mailen siger “i morgen”, slår den op i kalenderen
med `calendar.get_events`, og _kun_ hvis datoen er tom, oprettes en heldagsbegivenhed
med den rigtige affaldstype. Så bliver der aldrig dubletter, og der bliver aldrig
gættet på en dato, hvis forsyningen ændrer ordlyden.

Tabletkortet læser fra kalenderen – ikke fra mailen – så det virker også, hvis en
mail skulle gå tabt. Her lå endnu en fælde: **En kalenders attributter beskriver
den begivenhed, der er i gang nu**, ikke den næste. På en tømningsdag kører heldagsbegivenheden
fra 00 til 24 og skygger for morgendagens tømning. Da vi havde to tømninger i træk,
forsvandt kortet præcis når det skulle bruges. Løsningen er en lille automation,
der hvert kvarter kalder `calendar.get_events` for i dag og i morgen og skriver 
resultatet til en hjælper, f.eks. “⌊🌿⌉ Haveaffald — tømmes i morgen”.

![](https://www.vogh.dk/wp-content/uploads/2026/10/image-3.png)

## 3. Stemmebesked kl. 19

Mailen kommer om eftermiddagen, så kl. 19 samme aften er et godt tidspunkt – alle
er hjemme, og der er stadig tid til at køre spanden ud. Beskeden siges kun, hvis
flaget er sat, og flaget slås fra bagefter, så den kun siges én gang:

    ```nord
    alias: "Affald: stemmeannoncering kl. 19"
    mode: single
    triggers:
      - trigger: time
        at: "19:00:00"
    conditions:
      - condition: state
        entity_id: input_boolean.affald_afventer
        state: "on"
    actions:
      - action: assist_satellite.announce
        target:
          entity_id: assist_satellite.home_assistant_voice_092cb5_assist_satellite
        data:
          message: "{{ states('input_text.affald_besked') }}"
          preannounce: true
        continue_on_error: true
      - action: input_boolean.turn_off
        target:
          entity_id: input_boolean.affald_afventer
    ```

`preannounce` spiller en lille lyd først, så man når at lytte efter. `continue_on_error`
sørger for, at flaget stadig bliver slået fra, selv hvis højttaleren er offline –
ellers ville den gamle besked blive læst op aftenen efter.

## 4. “Kørt ud”-knappen

Er spanden allerede kørt ud, er der ingen grund til at blive mindet om det. På tabletkortet
er der derfor en knap, “Kørt ud”. Trykker man på den, slås flaget fra, og stemmebeskeden
springes over. Når påmindelsen slukker efter tømningen, nulstilles knappen igen:

    ```nord
    alias: "Affald: kvittering for kørt ud"
    mode: queued
    triggers:
      - trigger: state
        entity_id: input_boolean.skraldespand_kort_ud
        from: "off"
        to: "on"
        id: kvitteret
      - trigger: state
        entity_id: binary_sensor.affald_snart
        from: "on"
        to: "off"
        id: toemt
    actions:
      - choose:
          - conditions:
              - condition: trigger
                id: kvitteret
            sequence:
              - action: input_boolean.turn_off
                target:
                  entity_id: input_boolean.affald_afventer
          - conditions:
              - condition: trigger
                id: toemt
            sequence:
              - action: input_boolean.turn_off
                target:
                  entity_id: input_boolean.skraldespand_kort_ud
    ```

## 5. Kameraet tjekker efter

Kl. 18 aftenen før hver tømning – seks timer før kalenderens heldagsbegivenhed starter–
tager et af UniFi-kameraerne ved indkørslen et billede, og HACS-integrationen **
LLM Vision** spørger en AI-model, om affaldsbeholderne er kørt ud. Det er også derfor,
kalenderen skal være komplet – mangler en tømning, bliver tjekket aldrig kørt. Og
tidspunktet er ikke tilfældigt: kl. 18 er en time før stemmebeskeden.

### Tre fejl der gjorde kameraet blindt

Da jeg gravede i det for at skrive dette indlæg, viste det sig, at analysen **aldrig
var blevet kørt**:

 1. **Et tomt trin stoppede alt.** Første trin var et `ai_task.generate_data` uden 
    data – en rest fra en tidligere udgave. Home Assistant afviser kaldet, og automationen
    stopper, før analysen nås. Udefra så alt normalt ud: “sidst udløst kl. 18:00”, 
    ingen fejlbesked.
 2. **Udbyderen var slået fra.** AI-udbyderen i LLM Vision var deaktiveret, og LLM 
    Vision-hukommelsen pegede på et billede, der var slettet. Løsningen blev at oprette**
    Google Gemini** som ny udbyder og rydde op i hukommelsen.
 3. **Kameraet der både virker og ikke virker.** Det valgte kamera stod som `unavailable`,
    så mit første gæt var, at det var forkert. Men da jeg hentede billeder fra begge
    kameraer, var det netop det “utilgængelige”, der kunne se beholderne. Entiteten
    skifter til `recording` et splitsekund hvert tiende minut – men snapshot virker
    fint.

### Tæl i stedet for at spørge

Første udgave spurgte modellen direkte: Er beholderne kørt ud? Svaret skulle starte
med **JA**, **NEJ** eller **USIKKER**. Det gik galt på to måder. Modellen skrev _“
NEJ. Affaldsbeholderne står …”_ på én linje, så svaret måtte læses som første ord
i stedet for første linje. Og værre: Dagen efter svarede den **JA** til præcis samme
scene – beholderne stod på deres faste plads. Det var ikke kun modellens skyld. 
Spandene stilles ud ved vejen, og **den kan kameraet slet ikke se**. Spørgsmålet
var umuligt at besvare.

Nu spørger automationen om noget, kameraet _kan_ se: **Hvor mange beholdere står
på deres faste plads?** Normalt er der tre – to ved garagegavlen og én ved buskene.
Første linje skal kun være antallet med cifre, eller USIKKER, hvis det er for mørkt,
eller noget dækker. At tælle er en langt enklere opgave end at gætte på noget uden
for billedet. Svaret læses som første ord eller tal, efter at alt andet end bogstaver
og cifre er fjernet:

| Svar fra modellen | Antal | Betyder | 
| `3` + beskrivelse på ny linje | 3 | Alle står hjemme – intet sker | 
| `**2.** To ved gavlen` | 2 | En er kørt ud – “Kørt ud” slås til | 
| `USIKKER, mørkt` | – | Intet sker | 
| `tre` eller `Antal: 2` | – | Ikke et rent tal – intet sker | 
| (fejl / intet svar) | – | Intet sker |

### Koblet til “Kørt ud”

Mangler der en beholder, mens der er tømning på vej, slår automationen den samme“
Kørt ud”-hjælper til som knappen på tabletten – og stemmebeskeden kl. 19 springes
over. Alt andet ændrer ingenting. Det er bevidst asymmetrisk: Det værste, der kan
ske, er en påmindelse for meget – aldrig en for lidt.

Kravet om, at der skal være tømning på vej, kom efter en testkørsel. Uden det kunne
et forkert svar mellem to tømninger slå “Kørt ud” til – og så ville stemmebeskeden
ved _næste_ tømning blive sprunget over, fordi hjælperen først nulstilles efter 
en tømning.

### Tjek når der sker noget ved spanden

Kl. 18 er sikkerhedsnettet. Derudover kalder en **webhook** Home Assistant, når 
der sker noget ved skraldespanden. Er der tømning på vej, og er der ikke allerede
kvitteret, venter automationen 30 sekunder, så spanden er rullet væk, og starter
så den samme optælling. Bagefter holder den 10 minutters pause, hvor nye webhooks
ignoreres, så travlhed ved spanden ikke giver en strøm af AI-kald. Logikken findes
kun ét sted – webhook-automationen kalder bare kameraautomationen.

    ```nord
    alias: Analyze Trashcan Image
    mode: single
    variables:
      normalt_antal: 3   # beholdere på deres faste plads
    triggers:
      # Heldagsbegivenheder starter 00:00, så -6 t = kl. 18 aftenen før
      - trigger: calendar
        entity_id: calendar.affalds_kalender
        event: start
        offset: "-6:0:0"
    actions:
      - action: llmvision.image_analyzer
        continue_on_error: true
        response_variable: analyse
        data:
          provider: DIN_LLM_VISION_UDBYDER   # f.eks. Google Gemini
          image_entity:
            - camera.g6_turret_high_resolution_channel_2
          message: >-
            Billedet er fra et overvågningskamera ved en indkørsel med garager.
            Tæl affaldsbeholderne (sorte/grå containere på hjul) i billedet.
            Normalt står der {{ normalt_antal }}: to ved gavlen af garagelængen
            og én længere til venstre ved buskene.
            Første linje skal KUN være antallet som et tal med cifre.
            Kan du ikke se området tydeligt, så skriv USIKKER i stedet.
            Beskriv derefter kort på dansk, hvor beholderne står.
          store_in_timeline: true
          use_memory: false
          target_width: 1280
          max_tokens: 300
          response_format: text
      # Første ord/tal i svaret; antal = -1, hvis det ikke er et rent tal
      - variables:
          svar: >-
            {{ ((analyse.response_text if analyse is defined and analyse is mapping else '')
                | regex_replace('[^A-Za-zÆØÅæøå0-9]+', ' ') | trim | upper).split(' ')[0] }}
          antal: "{{ svar | int(-1) if svar is match('^[0-9]+$') else -1 }}"
      # Kun når der er tømning på vej, og en beholder mangler
      - if:
          - condition: state
            entity_id: binary_sensor.affald_snart
            state: "on"
          - condition: template
            value_template: "{{ 0 <= antal | int(-1) < normalt_antal }}"
        then:
          - action: input_boolean.turn_on
            target:
              entity_id: input_boolean.skraldespand_kort_ud
    ```

`continue_on_error` på analysen betyder, at automationen fortsætter, hvis kameraet
eller AI-tjenesten er nede – `antal` bliver -1, og den normale påmindelse kører.
Tjekket på `trigger.platform` gør, at automationen også kan testkøres manuelt, hvor
der ingen kalenderbegivenhed er.

## Det jeg har lært

 * **Stol ikke på mailens overflade.** Afsender, emnefelt og tekstformat var alle
   anderledes end forventet. Kig på det rå event i Udviklerværktøjer → Events, før
   du skriver automationen.
 * **Brug én kilde til sandheden.** Mailen er triggeren, men kalenderen er det, 
   alt andet læser fra.
 * **Spørg kun om det, kameraet kan se.** “Er spanden kørt ud?” kunne ikke besvares,
   fordi vejen er uden for billedet. “Hvor mange står på deres plads?” kan.
 * **Tænk på de stille fejl.** Et JSON-kald der fejler, en højttaler der er offline,
   en mail der aldrig markeres som læst – det er dem, der giver mærkelig opførsel
   uger senere.

Resultatet er, at ingen i familien behøver at holde styr på tømmekalenderen længere.
Huset siger til.

## Fugtstyret udsugning – når den indbyggede sensor ikke kan finde ud af det

- Link: https://www.vogh.dk/fugtstyret-udsugning-naar-den-indbyggede-sensor-ikke-kan-finde-ud-af-det/
- Udgivet: 2026-10-09T17:09:01+00:00
- Forfatter: ha-upload

På badeværelset på 1. sal sidder en **Fresh Intellivent Sky** udsugning. Den har
en indbygget fugtsensor og en funktion, der skal skrue op, når luftfugtigheden stiger.
Problemet er bare, at den aldrig gjorde det. Her er historien om, hvorfor – og hvordan
Home Assistant løste det med en ekstern sensor og én automation.

## Problemet: sensoren følger ikke rummet

Fresh-udsugningen er koblet på Home Assistant via Bluetooth med HACS-integrationen
_Fresh Intellivent Sky (BLE)_, så jeg kan se alle dens målinger. Da jeg lagde dens
fugtmåling ved siden af en almindelig IKEA TIMMERFLOTTE temperatur- og fugtsensor
i samme rum, var forskellen tydelig:

|  | Fresh (indbygget) | IKEA-sensor | 
| Til hverdag | 42–46 % | 43–63 % | 
| Under et bad | 42–46 % | op til 83 % |

Fresh’ens egen sensor rørte sig stort set ikke – heller ikke midt i et varmt bad.
Derfor startede fugtfunktionen aldrig, og spejlet duggede til hver gang. Sensoren
sidder formentlig et sted i kanalen, hvor den ikke ser den fugtige luft fra rummet.

## Løsningen: lad Home Assistant styre

I stedet for at stole på udsugningens egen logik bruger jeg IKEA-sensoren og lader
en automation styre Fresh’ens **konstant hastighed**:

 * **Tænd**: Når fugten har været over **70 %** i 2 minutter, sættes hastigheden
   op til 2050 rpm, og konstant hastighed slås til.
 * **Sluk**: Når fugten har været under **65 %** i 10 minutter, slås konstant hastighed
   fra, og hastigheden sættes tilbage til den normale 850 rpm.
 * **Genstart efter udfald**: Bluetooth-forbindelsen til Fresh’en falder ud en gang
   imellem. Kommer den tilbage, mens det stadig er fugtigt, tændes den igen.

Forskellen mellem 70 % og 65 % (hysterese) og de to forsinkelser sørger for, at 
udsugningen ikke hopper til og fra, når fugten svinger omkring grænsen. 70 % ligger
et godt stykke over det normale niveau på 43–63 %, så den starter kun, når der faktisk
bliver taget et bad.

## Automationen

Her er hele automationen. Vil du bruge den selv, skal du udskifte entity-id’erne
med dine egne sensorer og din udsugning.

    ```nord
    alias: Bad 1. sal – udsugning ved høj fugt
    mode: queued
    triggers:
      # Fugtigt i 2 minutter
      - trigger: numeric_state
        entity_id: sensor.timmerflotte_temp_hmd_sensor_humidity
        above: 70
        for:
          minutes: 2
        id: fugtig
      # Udsugningen kommer tilbage efter et Bluetooth-udfald
      - trigger: state
        entity_id: switch.fresh_fresh_1_sal_constant_speed
        from: unavailable
        to: "off"
        id: fugtig
      # Tørt igen i 10 minutter
      - trigger: numeric_state
        entity_id: sensor.timmerflotte_temp_hmd_sensor_humidity
        below: 65
        for:
          minutes: 10
        id: toer
    conditions: []
    actions:
      - choose:
          - conditions:
              - condition: trigger
                id: fugtig
              - condition: numeric_state
                entity_id: sensor.timmerflotte_temp_hmd_sensor_humidity
                above: 70
              - condition: state
                entity_id: switch.fresh_fresh_1_sal_constant_speed
                state: "off"
            sequence:
              - action: number.set_value
                target:
                  entity_id: number.fresh_fresh_1_sal_constant_speed
                data:
                  value: 2050
              - action: switch.turn_on
                target:
                  entity_id: switch.fresh_fresh_1_sal_constant_speed
          - conditions:
              - condition: trigger
                id: toer
              - condition: state
                entity_id: switch.fresh_fresh_1_sal_constant_speed
                state: "on"
            sequence:
              - action: switch.turn_off
                target:
                  entity_id: switch.fresh_fresh_1_sal_constant_speed
              - action: number.set_value
                target:
                  entity_id: number.fresh_fresh_1_sal_constant_speed
                data:
                  value: 850
    ```

## Detaljer der er værd at lægge mærke til

 * **Trigger-id’er**: Alle tre triggere deler én automation. Id’erne `fugtig` og`
   toer` afgør, hvilken gren i `choose` der køres.
 * **Tjek fugten igen**: Når udsugningen kommer tilbage efter et udfald, tjekker
   betingelsen, at det faktisk stadig er fugtigt, før den tænder.
 * **Kun hvis det giver mening**: Der tændes kun, hvis udsugningen er slukket, og
   slukkes kun, hvis den er tændt. Er den `unavailable`, sker der ingenting – den
   bliver fanget af udfalds-triggeren, når den kommer tilbage.
 * **Hastigheden først**: Hastigheden sættes op, _før_ konstant hastighed slås til,
   så den ikke først kører langsomt og så skruer op.
 * **`mode: queued`**: Kommer to triggere tæt på hinanden, køres de efter hinanden
   i stedet for at blive smidt væk.

## Resultat

Nu går udsugningen i gang et par minutter inde i badet og kører, indtil luften er
tør igen. Ingen knapper, ingen app og intet dugget spejl. Og lærdommen er generel:
Når en enheds indbyggede sensor ikke måler det rigtige sted, så lad en billig ekstern
sensor og Home Assistant tage over.

![](https://www.vogh.dk/wp-content/uploads/2026/10/image-2.png)

## Mit Home Assistant-setup – et hus der (næsten) passer sig selv

- Link: https://www.vogh.dk/mit-home-assistant-setup-et-hus-der-naesten-passer-sig-selv/
- Udgivet: 2026-10-09T17:05:13+00:00
- Forfatter: ha-upload

Jeg har kørt Home Assistant derhjemme i flere år, og det er efterhånden vokset fra“
lad os lige styre et par lamper” til at være husets nervesystem. Her er en oversigt
over, hvad installationen består af, og hvad den bruges til i hverdagen. De enkelte
projekter får deres egne indlæg, efterhånden som jeg får skrevet dem.

## Tallene

| Platform | Home Assistant OS på en lille x86-pc | 
| Version | 2026.10 | 
| Entiteter | ca. 3.000 | 
| Automationer | 74 (56 aktive) | 
| Lyskilder | ca. 170 | 
| Etager / rum | 3 etager, 22 områder | 
| Kameraer | 6 | 
| HACS-pakker | 58 |

## Fundamentet: netværk og radio

 * **UniFi** står for netværket – switche, access points og NAS – og er integreret
   direkte i HA, så jeg f.eks. kan genstarte en PoE-port fra et dashboard og se 
   hele netværkskortet live.
 * **Zigbee** kører via Zigbee2MQTT og en SLZB-06 netværkskoordinator, så radioen
   kan placeres midt i huset i stedet for at sidde i en USB-port.
 * **Thread/Matter** via OpenThread Border Router og Matter Server.
 * **Wi-Fi-enheder** som Shelly-relæer og ESPHome-enheder – ESPHome-enhederne opdaterer
   sig selv automatisk.
 * **Node-RED** og **Mosquitto (MQTT)** kører som add-ons ved siden af.

## Lys

Huset har **LK IHC** som fast installation, og den er koblet på Home Assistant, 
så alle de klassiske tryk og udtag kan indgå i automationer. Ovenpå ligger:

 * **Philips Hue** til spots og udendørs bevægelsessensorer
 * **Govee** permanent lights og lyskæder via MQTT – perfekt til julebelysning
 * Automatisk aftenlys efter solnedgang, og “sluk alt”, når ingen er hjemme
 * Natlys, der tænder dæmpet, hvis nogen står op om natten
 * Morgenlys på badet, der selv slukker efter 30 minutter

## Indeklima

 * **Radiatortermostater** i de vigtigste rum, styret fra HA
 * **Fugtstyret udsugning** på badeværelset: Udsugningen (Fresh Intellivent Sky 
   via Bluetooth) har sin egen fugtsensor, men den var ubrugelig. Nu styres den 
   af en separat IKEA-sensor – den starter ved 70 % og stopper ved 65 %.
 * Pollental og UV-indeks hentes, så man ved, hvordan dagen ser ud

## Sikkerhed og kameraer

 * **UniFi Protect** med G4 Doorbell Pro ved hoveddøren og flere kameraer udenfor
 * Person-, køretøjs- og dyregenkendelse sender notifikationer – men kun når det
   er relevant
 * **AI-billedanalyse (LLM Vision)**: Kameraet kigger på skraldespandene og kan 
   fortælle, om de er kørt ud
 * Alarmpanel i HA og en “ingen hjemme”-tilstand, der slukker og sikrer huset

## Energi og bil

 * **Spotpriser** fra Nord Pool og Energi Data Service, så store forbrugere kan 
   køre, når strømmen er billig
 * **Elbil (Škoda Elroq)** via Škodas officielle API med påmindelse om opladning
   og automatisk oplåsning til morgenturen
 * **Monta**-ladeboks integreret, så opladningen kan følges og styres

## Hverdagen

Det er her, det for alvor giver værdi for familien:

 * **Affald**: Mails fra forsyningen bliver automatisk læst, tømningsdatoer vises
   på tabletten, og kl. 19 aftenen før kommer en stemmebesked om, hvilken spand 
   der skal ud
 * **Madplan**: **Mealie** kører som add-on og står for opskrifter og madplan; dagens
   og morgendagens ret vises på dashboardet og ligger i kalenderen
 * **Husopgaver**: En to-do-liste i HA, der automatisk bliver til opgaver på GitHub
 * **Hvidevarer**: Samsung-maskinerne styres lokalt uden cloud, og der kommer besked,
   når opvasker, vaskemaskine og tørretumbler er færdige
 * **Pizza-timere** – ja, fire af dem. Vi tager pizzaaften seriøst.
 * **Robotstøvsuger og robotplæneklipper** (Deebot og Mammotion Yuka) overvåges 
   og styres fra HA
 * **Sundhed**: Garmin-data, træningskalender, vandindtag og vitaminer logges
 * **Plantelys** til indendørs planter på timer

## Stemme og AI

 * **Home Assistant Voice** og en væghængt tablet (via Voice Satellite) fungerer
   som stemmeassistenter
 * Lokal tale-til-tekst (Whisper) og tekst-til-tale (Piper) med openWakeWord, så
   meget kan køre uden skyen
 * **Claude** og **OpenAI** er koblet på som samtaleagenter, så man kan spørge mere
   frit end med faste kommandoer – f.eks. “hvad skal vi have at spise i morgen?”
 * Scripts som “Se TV”, “Lav mad” og “Sluk alt” kan kaldes med stemmen

## Dashboards

 * En **Lenovo-tablet** på væggen som fælles familiedashboard med madplan, affald,
   kalender og vejr
 * **Sonoff NSPanel Pro** som vægpanel
 * Mobilapp til alle i familien med tilstedeværelse via zoner
 * En **3D-plantegning** af stueetagen (NeonPlan 3D), hvor lys og sensorer ses live

![](https://www.vogh.dk/wp-content/uploads/2026/10/image.png)

## HACS – fællesskabets byggeklodser

En stor del af installationen bygger på **HACS** (Home Assistant Community Store),
hvor jeg har 58 pakker installeret. De vigtigste:

 * **Integrationer**: IHC (dingusdk), Fresh Intellivent Sky, LocalThings (Samsung),
   Monta, Mammotion, Garmin Connect, Energi Data Service, LLM Vision, Mealie, Spook
   og WebRTC Camera
 * **Kort til dashboards**: Bubble Card, Mushroom, button-card, card-mod, apexcharts-
   card, mini-graph-card, Calendar Card Pro og Timer Bar Card
 * **Temaer**: Frosted Glass og Mushroom Themes

## Læs mere om projekterne

 1. [Fugtstyret udsugning – når den indbyggede sensor ikke kan finde ud af det](https://www.vogh.dk/fugtstyret-udsugning-naar-den-indbyggede-sensor-ikke-kan-finde-ud-af-det/)
 2. [Affald fra mail til stemmebesked – sådan husker vi skraldespanden](https://www.vogh.dk/affald-fra-mail-til-stemmebesked-saadan-husker-vi-skraldespanden/)(
    inkl. AI-kameraet, der tæller skraldespande)
 3. [Mealie i Home Assistant – og fire fejl der aldrig viste sig](https://www.vogh.dk/mealie-i-home-assistant-og-fire-fejl-der-aldrig-viste-sig/)
 4. [LK IHC i Home Assistant – når den faste installation skal være smart](https://www.vogh.dk/lk-ihc-i-home-assistant-naar-den-faste-installation-skal-vaere-smart/)
 5. [Pizzadej med 72 timers koldhævning – styret af Home Assistant](https://www.vogh.dk/pizzadej-med-72-timers-koldhaevning-styret-af-home-assistant/)
 6. [Mine HACS-favoritter – de pakker huset ikke kunne undvære](https://www.vogh.dk/mine-hacs-favoritter-de-pakker-huset-ikke-kunne-undvaere/)

Har du spørgsmål eller idéer, så skriv endelig.
