I can also confirm this. However when I restarted the virtual machine with HAOS, note not a restart through HA itself, I was rewarded with about 30 notifications for a new bird. Which was simply my full list of species detected so far. Just a note, I can absolutely deal with that for the rare occasion I restart the VM.
That’s odd that it gets reset when you restart. I don’t have that problem.
Edit: ohhh. Because your BirdNET endpoint is down and it’s returning an empty payload.
Edit 2: I have ideas on how to work around this, but it will probably be in my post as there’s a bit I need to edit in it.
My blog post is up!
I hope it’s not a problem that I’m sharing a link to my blog here. I serve no ads, no affiliate links, and no money is made. I like sharing projects back with the community and make it my mission to keep everything as open as possible.
Cool things about the post.
- I’ve fixed the issue where the state is lost when the API is down (like when the machine first boots up) by using a helper
templatesensor to persist the data (and added version comments so I can track them). - The
command_linesensor that fetched data from the/api/v2/analytics/species/detections/newendpoint is no longer needed as we can get what we need (and more) from the/api/v2/analytics/species/summaryendpoint. I’ve updated this. - Other minor updates to the cards, sensors, and labels.
The blog post includes:
- Two
command_linesensors in Home Assistant that fetch data from the BirdNET-Go API - Using these sensors, I’ve created a handful of custom markdown cards in Home Assistant
- I’ve also created a few notification automations for things like specific birds, new species, or species that have made a return
- A bunch of other bonuses (like scripts to generate shareable videos from detections, my favorite bird sounds so far, and some cool bird pictures)
Not at all.
If it’s helpful, I managed to add the images to the markdown card:
{% if has_value('sensor.birdnet_daily_summary') %}
{% set species_data = state_attr('sensor.birdnet_daily_summary', 'species_list') %}
{% if species_data and species_data | count > 0 %}
| Image | Species | Count | Sparkline (6AM-6PM) | Last |
|:-----:|:--------|:-----:|:-------------------:|:----:|
{% for bird in species_data | sort(attribute='latest_heard', reverse=true) | sort(attribute='count', reverse=true) %}
{#- Basic Info -#}
{%- set time = bird.latest_heard -%}
{%- set name = bird.common_name -%}
{%- set count = bird.count -%}
{%- set species_code = bird.species_code %}
{%- set ebird_url = "https://ebird.org/species/" ~ species_code %}
{%- set thumbnail = bird.thumbnail_url if bird.thumbnail_url is defined else "" %}
{#--- Sparkline Calculation (Full Day Aggregated - 14 slots) ---#}
{%- set hourly = bird.hourly_counts -%}
{%- set sparkline_str = "N/A" -%} {#- Default value -#}
{%- if hourly is defined and hourly is iterable and hourly | count == 24 -%}
{#- Aggregate counts: [0-6], 6..18, [18-24] (14 slots total, safe concatenation) -#}
{%- set early_sum = hourly[0:6] | sum -%}
{%- set middle_part = hourly[6:18] -%}
{%- set late_sum = hourly[18:24] | sum -%}
{%- set aggregated_counts = [early_sum] + middle_part + [late_sum] -%}
{# Check if list has expected count before finding max #}
{%- if aggregated_counts | count == 14 -%}
{#- Find Max for Scaling -#}
{%- set max_val = aggregated_counts | max -%}
{#- Define Sparkline Characters (7 levels, U+2581 to U+2587) -#}
{%- set spark_chars = ['▁', '▂', '▃', '▄', '▅', '▆', '▇'] -%}
{%- set sparkline = namespace(text="") -%}
{%- for v in aggregated_counts -%}
{#- Map value to character index (0-6), scale factor 7, max index 6 -#}
{%- set char_index = ([0, (v * 7 / max_val)|int , 6] | sort)[1] if max_val > 0 else 0 -%}
{#- Append character -#}
{%- set sparkline.text = sparkline.text ~ spark_chars[char_index] -%}
{%- endfor -%}
{%- set sparkline_str = sparkline.text -%}
{%- else -%}
{% set sparkline_str = "?" * 12 %}
{%- endif -%}
{%- endif -%}
{#--- End Sparkline Calculation ---#}
{#- Output Row with Image -#}
| {% if thumbnail %}<img src="{{ thumbnail }}" width="40" height="40" style="border-radius: 50%; object-fit: cover;">{% else %}🐦{% endif %} | [{{ name }}]({{ ebird_url }}) | {{ count }} | `{{ sparkline_str }}` | {{ today_at(time) | as_timestamp | timestamp_custom('%H:%M', true) }} |
{% endfor %}
---
**{{ species_data | sum(attribute='count') | int }} Total Detections • {{ species_data | count }} Species Today**
{% else %}
No bird species data available or list is empty.
{% endif %}
{% else %}
BirdNET-Go API is not available.
{% endif %}
Have you noticed that something have been changed in latest birdnet-go updates that this card isn’t working anymore:
type: markdown
title: Tirpat
content: >-
Aika| Nimi|Havainnot tänään|
[Varmuus](http://192.168.1.133:8080/)
:---|:---|:---:|:---:
{%- set t = now() %}
{%- set bird_list = state_attr('sensor.birdnet_go_events','bird_events') |
sort(attribute='time', reverse=true) | map(attribute='name') | unique | list
%}
{%- set bird_objects = state_attr('sensor.birdnet_go_events','bird_events') |
sort(attribute='time', reverse=true) %}
{%- for thisbird in bird_list or [] %}
{%- set ubird = ((bird_objects | selectattr("name", "equalto", thisbird)) |
list)[0] %}
{%- set ubird_count = ((bird_objects | selectattr("name", "equalto",
thisbird)) | list) | length %}
{%- set ubird_max_confidence = ((bird_objects | selectattr("name", "equalto",
thisbird)) | map(attribute='confidence') | map('replace', '%', '') |
map('float') | max | round(0)) %}
{%- if ubird_max_confidence > 70 %}
{{ubird.time}}
| [{{ubird.name}}](https://fi.wikipedia.org/wiki/{{ubird.name |
replace(' ', '_')}}) | {{ubird_count}} | {{ ubird_max_confidence }} %
{%- endif %}
{%- endfor %}
Lajeja: {{bird_list | count}}
Havainnot: {{bird_objects | count}}
Seem that this sensor gives nowadays unavailable state, but have been working earlier:
template:
- trigger:
- trigger: mqtt
topic: "birdnet"
- trigger: time
at: "00:00:00"
id: reset
sensor:
- unique_id: c893533c-3c06-4ebe-a5bb-da833da0a947
name: BirdNET-Go Events
state: "{{ today_at(trigger.payload_json.Time) }}"
attributes:
bird_events: >
{% if trigger.id == 'reset' %}
{{ [] }}
{% else %}
{% set time = trigger.payload_json.Time %}
{% set name = trigger.payload_json.CommonName %}
{% set confidence = trigger.payload_json.Confidence|round(2) * 100 ~ '%' %}
{% set current = this.attributes.get('bird_events', []) %}
{% set new = dict(time=time, name=name, confidence=confidence) %}
{{ current + [new] }}
{% endif %}
I haven’t had any problems using the command_line sensor to populate the data in my card. I just updated to the nightly-20251012 build without any issue.
But my approach looks slightly different than yours. I think you’re still pulling all of the info from the MQTT sensor. There’s nothing wrong with doing it that way, but I took a different approach. My MQTT sensor is only there as a way to trigger my command_line sensor to run early if I know the data has been updated.
There have been a few bugs as well that I’ve stomped out over the past few months with the command_line sensor as well.
See the CHANGELOG at the top of my post if you want to switch over to that:
Hi Kyle, thanks for yor effort on this! And thanks, i’ll look to update my sensors and cards.
Post what the error is from your logs.
Managed to get it working again. For some reason mqtt message was stuck on several days old retained message. Deleted that and it’s working again.
Images are not showing anymore. Were you able to fix it?
If you updated birdnet-go nightly release a few weeks back, update it again. There were some API changes that kept the page from rendering correctly. Look at his blog post for details.
