Issue with displaying AI Generated Image ai_task.generate_image

Hi everyone,

I’m hoping someone can help me figure out a persistence issue with a daily AI image generator script I put together.

Right now, I have a working script that generates a daily AI image of a historic event using ai_task.generate_data and ai_task.generate_image. I am displaying this image on my dashboard using a trigger-based template image entity. It’s basically exactly what is shown in the official HA docs for weather visualization.

The Problem: Everything works perfectly when the script runs. However, because it relies on a triggered event, the image (and the template entity state) is completely lost every time I restart Home Assistant.

What I’ve Tried: I tried to save the generated_image.url to an input_text helper so I could restore or reference it upon restart. Unfortunately, this fails because the generated AI image URLs are incredibly long and exceed the 255-character limit of input_text entities.

Here is the script I am currently using:

alias: Historic AI Image
description: >-
  Generates a daily AI image of a historical event with explanation, stores
  result in helpers and fires event for dashboard display.
sequence:
  - action: ai_task.generate_data
    data:
      instructions: >-
        Today is {{ now().strftime('%B %d') }}. Find a fascinating, surprising,
        or significant historical event that happened on this exact day in
        history — from any field such as science, exploration, art, music,
        sports, politics, technology, or nature. It is extremly important that
        its accurate and it actually happend on that day.

        Also create a vivid image generation prompt for this event. Choose a
        creative and fitting art style (e.g. watercolor painting, oil painting,
        pencil sketch, comic book style, impressionist, art nouveau, woodblock
        print, charcoal drawing, stained glass, linocut, mosaic) — vary the
        style for visual diversity.

        The image must:
        - Capture the most dramatic or emotionally powerful moment of the event
        - Incorporate the date, year, and location visually into the composition
        - Be visually rich and detailed

        IMPORTANT: The title and description MUST be written in German. The
        image_prompt should remain in English.
      structure:
        description:
          description: >-
            Engaging 1-2 sentence description of the event in German, max 200
            characters
          required: true
          selector:
            text: {}
        image_prompt:
          description: >-
            Detailed vivid image generation prompt in English including art
            style, scene details, colors, composition, and date/location
            integration
          required: true
          selector:
            text: {}
        title:
          description: Short event title in German, max 60 characters
          required: true
          selector:
            text: {}
      task_name: Historic event research
      entity_id: ai_task.gemini_3_1_pro
    response_variable: event_info
  - action: ai_task.generate_image
    data:
      entity_id: ai_task.google_ai_task_images
      instructions: "{{ event_info.data.image_prompt }}"
      task_name: Generate AI Image of Historic Event
    response_variable: generated_image
  - event: historic_ai_image_generated
    event_data:
      url: "{{ generated_image.url }}"
      title: "{{ event_info.data.title }}"
      description: "{{ event_info.data.description }}"
  - action: input_text.set_value
    data:
      entity_id: input_text.historic_event_title
      value: "{{ event_info.data.title }}"
  - action: input_text.set_value
    data:
      entity_id: input_text.historic_event_description
      value: "{{ event_info.data.description }}"
  - action: input_text.set_value
    data:
      entity_id: input_text.historic_event_image_url
      value: "{{ generated_image.url.split('?')[0] }}"

This is what my template.yaml looks like:

- trigger: 
    - alias: "Update image when a new historic image is generated"
      trigger: event
      event_type: historic_ai_image_generated
  image:
    - name: "Historic AI generated image of Today"
      unique_id: historic_ai_image_today
      url: "http://localhost:8123{{ trigger.event.data.url }}"

Has anyone found a clever workaround to keep these trigger-based AI images visible after a reboot? Is there a better way to store long URLs, or a simple way to actually download/save the image file locally to the www folder as part of the script?

Thanks in advance for any advice!

Regards
Stefan

Well, since it’s AI written, you should ask the AI where it’s stored. If it’s not a place that survives reboot it should fix that.
Not may of us like to help with LLM code.

Well I am using the standard ai_task.generate_image action which accoding to the docs AI Task - Home Assistant is returning a URL “The URL of the generated image, without the host part. The URL is only valid for one hour.” as a response.

I also do get that respones and can store it in an Trigger based Template Image and use that template image in a dashboard.
All of that is working. I also know where the file is and can access it via the Media Sources its stored under media/ai_task

The issue is that as far as I´m aware of there is now way to use this in a dashboard. You have to either have a trigger based image template (which is lost after each restart) or something else which is the reason why I am asking here if anyone else has an idea.

Btw. of course some of that script was also written by AI, but simply because AI is much faster an better than me at this, but this actually has nothing to do with my issue.

I ended up finding a workaround myself and wanted to share it here in case anyone runs into the same issue.

The core problem was that the image URL generated by the AI image entity is dynamic and temporary, making it difficult to use in a dashboard card with a fixed reference. On top of that, the image template entity is quite limited in general – most notably, it loses its state (and with it the generated image) after every HA restart, and as far as I know there is currently no workaround for that.

My solution: I added the Downloader integration and created a downloads folder in the main config folder. Whenever a new image is generated, I call a service action that downloads the file to that fixed location with a static filename:

action: downloader.download_file
data:
  overwrite: true
  url: http://localhost:8123{{ generated_image.url }}
  filename: latest_ai_image_of_historic_event.png

Then in the dashboard I reference it via a picture card using the stable media-source URL:

type: picture
image:
  media_content_id: media-source://media_source/downloads/latest_ai_image_of_historic_event.png
  media_content_type: image/png

This way the filename never changes, so the dashboard card always points to the right place.

One remaining limitation: After a new image is downloaded and overwrites the old one, I still have to manually refresh the browser/dashboard for the updated image to actually show up. The card doesn’t seem to detect that the file content changed since the URL stays the same.

Wondering if anyone has a cleaner solution for this – or a way to force the picture card to reload without a full page refresh?

OK. If you have trouble connecting to an image in the media-source location, there is a post that might help.
Don’t forget to find a way to delete those images after a time, or they might fill up your file system and they might be in the path of the back-up, making that grow out of control.

you could have easily used the snapshot action

@Sir_Goodenough Thanks for the help and that post! It definitely helped, at least by getting me to stop saving this in my config folder. I just made a sub-folder in the media folder instead and I’m using the snapshot function instead of the downloader there now.

Since it unfortunately doesn’t solve the main issue (template image entities with a trigger still disappearing after a restart), I’m just going with a workaround. I’ll save it via snapshot and use that as a fallback on my dashboard: if the dynamic image fails to load, I’ll just display the fixed one.

Also deleting it daily so that only one image is saved there and the folder doesnt get bloated with the images.

@VietNgoc Thanks a lot for that input! Definately a smarter way to save that instead of the using the Downloader integration as I did before.

1 Like

Since the original issue unfortunately still persists (template image entities with a trigger disappearing after a restart), I decided to just go with a workaround. I now save the image via snapshot and use it as a backup/fallback. If the dynamic image fails to load or goes unavailable, I just display the fixed static one.

For anyone curious how I managed to set this all up, here is the full breakdown:

First, I added this to my configuration.yaml so I have direct access to the media folder:

homeassistant:
  media_dirs:
    local: /media

Inside the media folder, I created a new subfolder called static_file_names. That’s where I always save the permanent image with the exact same static filename.

Here is my updated script:

alias: Historic AI Image
description: >-
  Generates a daily AI image of a historical event with explanation, stores
  result in helpers and fires event for dashboard display.
sequence:
  - action: ai_task.generate_data
    data:
      instructions: >-
        Today is {{ now().strftime('%B %d') }}. Find a fascinating, surprising,
        or significant historical event that happened on this exact day in
        history — from any field such as science, exploration, art, music,
        sports, politics, technology, or nature. It is extremly important that
        its accurate and it actually happend on that day.

        Also create a vivid image generation prompt for this event. Choose a
        creative and fitting art style (e.g. watercolor painting, oil painting,
        pencil sketch, comic book style, impressionist, art nouveau, woodblock
        print, charcoal drawing, stained glass, linocut, mosaic) — vary the
        style for visual diversity.

        The image must:
        - Capture the most dramatic or emotionally powerful moment of the event
        - Incorporate the date, year, and location visually into the composition
        - Be visually rich and detailed

        IMPORTANT: The title and description MUST be written in German. The
        image_prompt should remain in English.
      structure:
        description:
          description: >-
            Engaging 1-2 sentence description of the event in German, max 200
            characters
          required: true
          selector:
            text: {}
        image_prompt:
          description: >-
            Detailed vivid image generation prompt in English including art
            style, scene details, colors, composition, and date/location
            integration
          required: true
          selector:
            text: {}
        title:
          description: Short event title in German, max 60 characters
          required: true
          selector:
            text: {}
      task_name: Historic event research
      entity_id: ai_task.google_gemini_3_1_pro_preview
    response_variable: event_info
  - action: ai_task.generate_image
    data:
      entity_id: ai_task.google_ai_task_images
      instructions: "{{ event_info.data.image_prompt }}"
      task_name: Generate AI Image of Historic Event
    response_variable: generated_image
  - parallel:
      - event: historic_ai_image_generated
        event_data:
          url: "{{ generated_image.url }}"
          title: "{{ event_info.data.title }}"
          description: "{{ event_info.data.description }}"
      - action: input_text.set_value
        data:
          entity_id: input_text.historic_event_title
          value: "{{ event_info.data.title }}"
      - action: input_text.set_value
        data:
          entity_id: input_text.historic_event_description
          value: "{{ event_info.data.description }}"
  - delay:
      seconds: 2
  - action: image.snapshot
    target:
      entity_id: image.historic_ai_generated_image_of_today
    data:
      filename: /media/static_file_names/latest_ai_image_of_historic_event.png

And here is what my dashboard configuration looks like to tie it all together with the fallback logic:

type: grid
cards:
  - type: heading
    heading: Heute in der Geschichte
    heading_style: title
    tap_action:
      action: perform-action
      perform_action: script.turn_on
      target:
        entity_id: script.historic_ai_image
      data: {}
    badges:
      - type: entity
        show_state: false
        show_icon: true
        entity: script.historic_ai_image
        color: state
        state_content: current
    icon: mdi:newspaper-variant-multiple-outline
  - type: picture
    image_entity: image.historic_ai_generated_image_of_today
    visibility:
      - condition: and
        conditions:
          - condition: state
            entity: image.historic_ai_generated_image_of_today
            state_not: unavailable
          - condition: state
            entity: image.historic_ai_generated_image_of_today
            state_not: unknown
  - type: picture
    visibility:
      - condition: or
        conditions:
          - condition: state
            entity: image.historic_ai_generated_image_of_today
            state: unavailable
          - condition: state
            entity: image.historic_ai_generated_image_of_today
            state: unknown
    image:
      media_content_id: >-
        media-source://media_source/local/static_file_names/latest_ai_image_of_historic_event.png
      media_content_type: image/png
      metadata:
        title: latest_ai_image_of_historic_event.png
        thumbnail: null
        media_class: image
        children_media_class: null
        navigateIds:
          - {}
          - media_content_type: app
            media_content_id: media-source://media_source
          - media_content_type: ""
            media_content_id: media-source://media_source/local/static_file_names
  - type: markdown
    grid_options:
      columns: 12
      rows: auto
    content: |-
      **{{ states('input_text.historic_event_title') }}**

      {{ states('input_text.historic_event_description') }}

I am currently working on turning this whole setup into a Blueprint to make it easily accessible and simple to install for everyone. I will probably share it once it is polished!

2 Likes