[How-To] Fetch and display Garbage Collection (Sophämtning) for Stockholm, Sweden (SVOA API)

Hi everyone,

I noticed there is a lack of clear documentation on how to reliably fetch and display garbage collection data specifically for Stockholm, Sweden (Stockholm Vatten och Avfall / SVOA). Instead of relying on third-party custom components that might break, I found a way to query their API directly using Home Assistant’s native REST sensor.

I wanted to share a complete guide on how to get this data, along with a bonus template for a very compact and dynamic dashboard card.

Step 1: Finding your API URL

SVOA uses a simple open API endpoint for searches, but your address needs to be URL-encoded. The base URL is: https://www.stockholmvattenochavfall.se/villa-och-radhus/avfallstjanster/nar-kommer-sopbilen/Search?address=

Add your address, area, and zip code to the end, replacing spaces with %20 and Swedish letters with their URL-encoded counterparts (å = %C3%A5, ä = %C3%A4, ö = %C3%B6). Example for “Exempelgatan 1, Stockholm, 12345”: .../Search?address=Exempelgatan%201,%20Stockholm,%2012345

Step 2: The REST Configuration

You can add this directly under the rest: domain in your configuration.yaml, or in a separate rest.yaml file (if you use rest: !include rest.yaml).

This code fetches the JSON data every 6 hours and uses Jinja loops to dynamically find the correct waste types (General, Food, and Garden waste) and extract the Date, Frequency, and Weekday.

- resource: "https://www.stockholmvattenochavfall.se/villa-och-radhus/avfallstjanster/nar-kommer-sopbilen/Search?address=Miklag%C3%A5rdsv%C3%A4gen%2012,%20Bromma,%2016855"
  scan_interval: 21600
  headers:
    User-Agent: "Mozilla/5.0"
    Accept: "application/json"
  sensor:
    # ==========================================
    # 1. RESTAVFAfellowLL
    # ==========================================
    - name: "Soptömning Restavfall Datum"
      unique_id: soptomning_restavfall_datum
      icon: mdi:trash-can
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Restavfall Frekvens"
      unique_id: soptomning_restavfall_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Restavfall Veckodag"
      unique_id: soptomning_restavfall_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 2. MATAVFALL
    # ==========================================
    - name: "Soptömning Matavfall Datum"
      unique_id: soptomning_matavfall_datum
      icon: mdi:food-apple
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Matavfall Frekvens"
      unique_id: soptomning_matavfall_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Matavfall Veckodag"
      unique_id: soptomning_matavfall_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 3. TRÄDGÅRDSAVFALL
    # ==========================================
    - name: "Soptömning Trädgård Datum"
      unique_id: soptomning_tradgard_datum
      icon: mdi:leaf
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Trädgård Frekvens"
      unique_id: soptomning_tradgard_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Trädgård Veckodag"
      unique_id: soptomning_tradgard_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

Restart Home Assistant after adding this to generate the sensors.

Step 3: The Dashboard Card (Bonus)

If you have a wall-mounted tablet (Kiosk mode), standard cards often take up too much space. Here is a highly compressed, dynamic card to display the data we just fetched.

Features of this card:

  • Icon colors change automatically (Grey = Winter break/inactive, Blue/Brown/Green = Normal, Orange = 3 days left, Red = 1 day left or Today).
  • Injects a dynamic countdown text right next to the name (Idag), (Imorgon), (Om X dgr), or (Vinteruppehåll).

Requirements for the card: Install template-entity-row and card-mod via HACS Frontend.

The Code:

type: entities
title: Avfallshämtning
card_mod:
  style: |
    #states > * {
      margin-top: -10px !important;
      margin-bottom: -10px !important;
    }
entities:
  - type: custom:template-entity-row
    entity: sensor.soptomning_restavfall_datum
    icon: mdi:trash-can
    name: >
      {% set datum = states('sensor.soptomning_restavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} Restavfall (Ej aktiv) {% elif
      dagar <= 0 %} Restavfall (Idag) {% elif dagar == 1 %} Restavfall (Imorgon)
      {% else %} Restavfall (Om {{ dagar }} dgr) {% endif %}
    state: >
      {{ states('sensor.soptomning_restavfall_veckodag') }} {{
      states('sensor.soptomning_restavfall_datum') }}
    color: >
      {% set datum = states('sensor.soptomning_restavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} grey {% elif dagar <= 0 %} red
      {% elif dagar <= 3 %} orange {% else %} dodgerblue {% endif %}
  - type: custom:template-entity-row
    entity: sensor.soptomning_matavfall_datum
    icon: mdi:food-apple
    name: >
      {% set datum = states('sensor.soptomning_matavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} Matavfall (Ej aktiv) {% elif
      dagar <= 0 %} Matavfall (Idag) {% elif dagar == 1 %} Matavfall (Imorgon)
      {% else %} Matavfall (Om {{ dagar }} dgr) {% endif %}
    state: >
      {{ states('sensor.soptomning_matavfall_veckodag') }} {{
      states('sensor.soptomning_matavfall_datum') }}
    color: >
      {% set datum = states('sensor.soptomning_matavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} grey {% elif dagar <= 0 %} red
      {% elif dagar <= 3 %} orange {% else %} brown {% endif %}
  - type: custom:template-entity-row
    entity: sensor.soptomning_tradgard_datum
    icon: mdi:leaf
    name: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej säsong'] %} Trädgård (Ej säsong) {% elif
      dagar > 30 %} Trädgård (Vinteruppehåll) {% elif dagar <= 0 %} Trädgård
      (Idag) {% elif dagar == 1 %} Trädgård (Imorgon) {% else %} Trädgård (Om {{
      dagar }} dgr) {% endif %}
    state: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% if datum
      in ['unavailable', 'unknown', 'Ej säsong'] %} 
        -
      {% else %}
        {{ states('sensor.soptomning_tradgard_veckodag') }} {{ datum }}
      {% endif %}
    color: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej säsong'] or dagar > 30 %} grey {% elif
      dagar <= 0 %} red {% elif dagar <= 3 %} orange {% else %} #4CAF50 {% endif
      %}
view_layout:
  position: sidebar

Hope this helps anyone who need a garbage collect card in Stockholm out! Let me know if you have any questions.

Thank you, i was just thinking about adding collection dates to my dash.
SVOA seems to have updated the GET url input, the special åäö characters are now to be entered in the address directly without translation to "%C3%A5".