Wanted to share my dashboards for my BirdWeather PUC, that I call Hermes. This is an amazing device that allows you to "easily connect your PUC to your home WiFi, and it will continuously monitor the sounds in your backyard, identifying bird songs in real-time." Even better, the device has an open API that allows you to pull the data in and visualize in a dashboard.
BirdWeather site: https://www.birdweather.com/
BirdWeather API docs: BirdWeather API
I have created two dashboards so far for a Fire HD10 tablet. One is a daily view of bird activity and the second is an Almanac view showing weekly data trends of bird species.
I will include the sensor config and pastebin links for both dashboards to help any new users expedite their birding journey. I am not a professional developer, so any errors or mistakes, my bad.
In the sensor.config, I have noted the specific paramaters you will need to update with your station specific info like API, Station ID, Lat, Long, Timezone, etc. You can obtain the API Token and Station ID from the Advanced Settings in the BirdWeather App.
Please feel free to ask any questions. I can't promise I can answer them, but I hope this helps anyone enjoy the amazing world of birds right outside your door!
Prerequisite cards for my dashboards to work: custom:mushroom-title-card, custom:mushroom-template-card, custom:apexcharts-card, custom:button-card, and card-mod
Daily - title: ðŠķ Aviaryviews: - title: Aviary path: birdweather icon: mdi - Pastebin.com
Almanac - title: ð Almanacviews: - title: Almanac path: birdweather-almanac - Pastebin.com
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# BIRDWEATHER SENSOR CONFIG
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# Before using, replace the following placeholders:
# YOUR_API_KEY â BirdWeather API token (Advanced Settings in the BirdWeather app)
# YOUR_STATION_ID â BirdWeather Station ID (Advanced Settings in the BirdWeather app)
# YOUR_UTC_OFFSET â Your UTC offset, e.g. -05:00 for ET standard, -04:00 for ET daylight
# YOUR_NE_LAT â Northeast latitude of your regional bounding box (~50mi radius)
# YOUR_NE_LON â Northeast longitude of your regional bounding box
# YOUR_SW_LAT â Southwest latitude of your regional bounding box
# YOUR_SW_LON â Southwest longitude of your regional bounding box
#
# To find your bounding box coordinates, use: https://boundingbox.klokantech.com/
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# Latest 100 detections â drives Most Recent + Recent Detections cards
- platform: rest
name: Latest Bird Detections
resource: https://app.birdweather.com/api/v1/stations/YOUR_STATION_ID/detections/?limit=100&order=desc&token=YOUR_API_KEY
value_template: "{{ value_json.detections[0].species.commonName }}"
json_attributes:
- detections
scan_interval: 60
# Top species by count for the current day â drives Top Species Today cards
- platform: rest
name: Top 100 Bird Species
resource: https://app.birdweather.com/graphql
method: POST
headers:
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
payload_template: >-
{"query": "{ station(id: \"YOUR_STATION_ID\") { topSpecies(limit: 100, period: { count: {{ [now().hour, 1] | max }}, unit: \"hour\" }) { count species { commonName imageUrl } } } }"}
value_template: "{{ value_json.data.station.topSpecies[0].species.commonName }}"
json_attributes_path: "$.data.station"
json_attributes:
- topSpecies
scan_interval: 300
# Daily species count â number of distinct species detected today
- platform: rest
resource_template: "https://app.birdweather.com/api/v1/stations/YOUR_STATION_ID/species?period=day&from={{ now().replace(hour=0,minute=0,second=0,microsecond=0).strftime('%Y-%m-%d') }}T00:00:00YOUR_UTC_OFFSET&token=YOUR_API_KEY"
scan_interval: 300
name: "Bird Daily Species"
unique_id: bird_daily_species
value_template: >
{% set today = now().strftime('%Y-%m-%d') %}
{{ value_json.species | selectattr('latestDetectionAt', 'search', today) | list | length }}
# Daily detection count â total detections across all species today
- platform: rest
resource_template: "https://app.birdweather.com/api/v1/stations/YOUR_STATION_ID/species?period=day&from={{ now().replace(hour=0,minute=0,second=0,microsecond=0).strftime('%Y-%m-%d') }}T00:00:00YOUR_UTC_OFFSET&token=YOUR_API_KEY"
scan_interval: 300
name: "Bird Daily Detections"
unique_id: bird_daily_detections
value_template: "{{ value_json.species | map(attribute='detections') | map(attribute='total') | sum }}"
# Weekly species count â distinct species since the most recent Sunday
# scan_interval aligned to 300 (matches daily) to prevent Sunday rollover
# mismatch caused by stale hourly data sitting next to fresh 5-min daily data.
- platform: rest
resource_template: >-
https://app.birdweather.com/api/v1/stations/YOUR_STATION_ID/species?period=day&limit=100&from={{
(now().date() - timedelta(days=(now().weekday() + 1) % 7)).strftime('%Y-%m-%d')
}}T00:00:00YOUR_UTC_OFFSET&token=YOUR_API_KEY
scan_interval: 300
name: "Bird Weekly Species"
unique_id: bird_weekly_species
value_template: "{{ value_json.species | length }}"
# Weekly detection count â total detections since the most recent Sunday
# scan_interval aligned to 300 for the same reason as above.
- platform: rest
resource_template: >-
https://app.birdweather.com/api/v1/stations/YOUR_STATION_ID/species?period=day&limit=100&from={{
(now().date() - timedelta(days=(now().weekday() + 1) % 7)).strftime('%Y-%m-%d')
}}T00:00:00YOUR_UTC_OFFSET&token=YOUR_API_KEY
scan_interval: 300
name: "Bird Weekly Detections"
unique_id: bird_weekly_detections
value_template: "{{ value_json.species | map(attribute='detections') | map(attribute='total') | sum }}"
- platform: rest
name: "Bird Hourly Activity"
unique_id: bird_hourly_activity
resource: https://app.birdweather.com/graphql
method: POST
headers:
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
payload_template: >-
{"query": "{ timeOfDayDetectionCounts(stationIds: [YOUR_STATION_ID], period: {count: {{ [now().hour, 1] | max }}, unit: \"hour\"}) { bins { key count } count species { commonName } } }"}
value_template: "{{ value_json.data.timeOfDayDetectionCounts | length }}"
json_attributes_path: "$.data"
json_attributes:
- timeOfDayDetectionCounts
scan_interval: 300
- platform: rest
name: bird_earliest_detection_today
resource: "https://app.birdweather.com/graphql"
method: POST
headers:
Authorization: Bearer YOUR_API_KEY
Content-Type: "application/json"
payload: "{\"query\": \"query { detections(last: 1, stationIds: [YOUR_STATION_ID], period: {count: 1, unit: \\\"day\\\"}) { nodes { timestamp } } }\"}"
value_template: >
{{ value_json.data.detections.nodes[0].timestamp
if value_json.data.detections.nodes | length > 0
else '' }}
scan_interval: 300
# Regional top species â bounding box defines your ~50mi search radius
# Generate your coordinates at: https://boundingbox.klokantech.com/
- platform: rest
name: bird_regional_top_species
resource: "https://app.birdweather.com/graphql"
method: POST
headers:
Authorization: Bearer YOUR_API_KEY
Content-Type: "application/json"
payload: >
{"query": "query { topSpecies(limit: 5, ne: {lat: YOUR_NE_LAT, lon: YOUR_NE_LON}, sw: {lat: YOUR_SW_LAT, lon: YOUR_SW_LON}) { count species { commonName scientificName imageUrl } } }"}
value_template: "{{ value_json.data.topSpecies | length }}"
json_attributes_path: "$.data"
json_attributes:
- topSpecies
scan_interval: 3600
- platform: rest
name: bird_weekly_stats
resource: https://app.birdweather.com/graphql
method: POST
headers:
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
payload: >
{"query": "{ dailyDetectionCounts(period: {count: 7, unit: \"day\"}, stationIds: [YOUR_STATION_ID]) { date total counts { count species { commonName } } } }"}
value_template: "{{ value_json.data.dailyDetectionCounts | length }}"
json_attributes_path: "$.data"
json_attributes:
- dailyDetectionCounts
scan_interval: 3600
- platform: rest
name: bird_monthly_stats
resource: https://app.birdweather.com/graphql
method: POST
headers:
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
payload: >
{"query": "{ dailyDetectionCounts(period: {count: 30, unit: \"day\"}, stationIds: [YOUR_STATION_ID]) { date total counts { count species { commonName } } } }"}
value_template: "{{ value_json.data.dailyDetectionCounts | length }}"
json_attributes_path: "$.data"
json_attributes:
- dailyDetectionCounts
scan_interval: 3600

