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.
