--- title: 'BirdNET-PI & HomeAssistant: Part 2' date: 2023-10-04T10:35:23-04:00 tags: ["homeassistant", "diy", "selfhosted"] categories: ["Tutorial"] author: "Me" showToc: true TocOpen: false draft: false hidemeta: false description: 'A Follow up from the previous post, this tutorial takes all the sensors we created and loads them into a beautiful dashboard!' disableHLJS: true disableShare: false disableHLJS: false hideSummary: false searchHidden: true ShowReadingTime: true ShowBreadCrumbs: true ShowPostNavLinks: true ShowWordCount: true ShowRssButtonInSectionTermList: true UseHugoToc: true ShowBreadCrumbs: true cover: image: "birdnet-homeassistant-part2.png" alt: "Part 2 of my foray into HomeAssistant dashboard featuring BirdNET-Pi Sensors" caption: "Part 2 of my foray into HomeAssistant dashboard featuring BirdNET-Pi Sensors" relative: false hidden: true --- ## Checking for Entities If you're following up on this from [my first post]({{< ref "posts/birdnet_homeassistant.md" >}}), you've already added your AppDaemon script and confirmed that the AppDaemon logs don't show any errors. Now is the true test if it's working: do you have the new sensors in HomeAssistant?! {{< box info >}} The best way to do this is by just type `e` from any screen in the HomeAssistant UI! That will bring up a list of entities. Start typing "bird" or "birdnet" and you should see the new entities listed there. {{< /box >}} ## Dashboard Overview & Dependencies Now that we have the correct entities, lets take a look at what we're working with. Full disclosure that once I got this working, I haven't really revisited it, refactored it, or made any improvements. I'm sure you'll find ways to use less YAML, but I wanted to get this out there sooner than later! ![HomeAssistant BirdNet-Pi Dashboard - Full View](../posts/img/birdnet-homeassistant-dash-full.png) I've included the code for all the cards at the bottom of this post. You can find them [here]({{< ref "birdnet_homeassistant_part2.md#dashboard-yaml" >}}). This dashboard is pretty simple, it brings in almost all of the sensors we created in the first post and organizes them in an as-pleasant-as-possible view. I'm definitely not a designer, so some of the colors could be worked on... Sensors in the dashboard: * [Overview Card:]({{< ref "birdnet_homeassistant_part2#overview-card">}}) * sensor.bird_common_name (only used to generate the picture) * camera.birdnet_flickr * sensor.bird_common_name * sensor.bird_science_name * Data Card: * sensor.bird_time_seen * sensor.bird_confidence * sensor.bird_last_seen * Weather Card: * weather.pirateweather * Description Card: * sensor.birdnet_wiki There are also two HomeAssistant dashboard dependencies that you'll need for this dashboard: * [Custom Weather Card](https://github.com/bramkragten/weather-card) * [Custom Button Card](https://github.com/custom-cards/button-card) * [Custom Card Mod](https://github.com/thomasloven/lovelace-card-mod) Now that we've got that squared away, let's jump into each card. ## Overview Card ![Overview Card](../posts/img/birdnet-homeassistant-overview-card.png) You'll notice from the few dependencies listed above, that I use this button card. **A lot.** [RomRider](https://github.com/RomRider) did a fantastic job of adding in a ton of flexibility into the card. For the overview card, we're taking one of the entities, in this case, `bird_common_name` and attaching the Flickr picture/sensor to it. Then, on the right, I'm displaying the name and common name. Here's the overview card's yaml. The tricky or tedious part of this card is making sure most of the card's attributes (icon, name, state, label) are set to false. Another option for the Common and Scientific name on the right is to use a markdown card. I couldn't get the formatting just right when using that card and some grid-card tricks, so I opted to reuse the super flexible button-card. Pay attention to the styles for the image (lines 24-40). Those are what keep the border around the image along with the image a certain height and width so that it looks proportional on the page. One additional thing I have been toying with but hadn't finalized was messing with `[img_cell][border]`. By adding some javascript in that section (see other cards), you could change the color of the border based on another parameter. Perhaps you look at the state of `sensor.bird_common_name` and if there is the name of a color in there, that's the border's color. Feel free to get crazy and creative with this! {{< details "Overview Card YAML" >}} ```yaml type: horizontal-stack cards: - type: custom:button-card entity: sensor.bird_common_name triggers_update: all show_name: false show_icon: false show_state: false show_label: false styles: card: - background: transparent - border: none - width: 215px - height: 175px custom_fields: picture: card: type: custom:button-card entity: camera.birdnet_flickr show_entity_picture: true show_name: false show_icon: false styles: card: - height: 100% - width: 100% - padding: 0px 15px 0px 15px - border-radius: 3px 3px 15px 3px - border: none - background: transparent - overflow: visible img_cell: - width: 180px - height: 160px - border-radius: 69% - border: 3px solid grey entity_picture: - width: 215px - height: 100% - type: vertical-stack cards: - type: custom:button-card entity: sensor.bird_common_name show_entity_picture: true show_state: true show_name: false show_icon: false styles: card: - background: transparent - border: none - margin-top: 35px - font-size: 25px - width: auto - type: custom:button-card entity: sensor.bird_science_name show_entity_picture: true show_state: true show_name: false show_icon: false styles: card: - background: transparent - border: black - width: auto ``` {{< /details>}} ## Data Card ![Data Card](../posts/img/birdnet-homeassistant-data-card.png) This card is fairly straight forward in that it's showing 3 key data points: the time of detection, detection confidence, how long ago it was seen. This could be fairly redundant since we already have the time of detection, but when you're just quickly glancing at the dashboard, minutes ago is much faster brain processing than comparing the timestamp and the current time. You've likely picked up by now that in the previous post, we never sent a payload to create the `sensor.bird_last_seen` entity. Here's how you can do it. ### Creating Bird Last Seen Entity When I first set out to create this sensor, I was messing with jinja templates for timestamp, datetime, strptime, and more. Here are a few code blocks I saved in my notes in case I went too far down the wrong path. Here are a few of them. ```jinja {{ now() - state_attr(sensor.bird_time_seen, 'last_triggered') > timedelta(hours=24) }} ``` or: ```jinja {% set bird = strptime(states('sensor.bird_time_seen'), "%H:%M:%S") %} {{ relative_time(bird) }} ``` The thing is, HomeAssistant has already implemented this really neat feature for calculating time, especially from when something was last updated. This function is called `relative_time`. Having something like this allows you set automations to run after a specific amount of time has passed since the last time a sensor or entity was updated. {{< box tip >}} An idea! 💡 For our specific use case, you could set up an automation that sends you a notification if no birds have been detected for over 30 minutes. Of course, we'll set parameters like not to notify you at night or during the winter months. {{< /box >}} The issue I faced with relative time has to do with the sensors I created from my AppDaemon script. Relative time expects a date _and_ time. I was only passing the time. In Home Assitant if you use the Developer Tools > Template to test relative time out on the `sensor.bird_time_seen` sensor, you'll get a result of 126 years... That's because without a date, Home Assistant defaults the date to `1900-01-01`. The full relative_time return is `1900-01-01 15:15:15`. We could go back and set the sensors to include both date and time, but I prefer them separate so that I can use them in different places. For this dashboard, the day is always today, so having the date felt redundant. To create a new sensor using the `relative_time` function, you'll need to edit your `configuration.yaml`. Once you're editing your config file, add the following: ```yaml template: # Bird Time Last Seen - sensor: - name: "Bird Last Seen" state: > {% set birdseen = (states('sensor.bird_date_seen')+' '+states('sensor.bird_time_seen')) %} {% set bird = relative_time(strptime(birdseen, '%Y-%m-%d %H:%M:%S')) %} {{ bird }} ``` What this does is uses Home Assistant's templating functionality and creates a new sensor called "Bird Last Seen". The default `sensor.` name will be `sensor.bird_last_seen`. To configure the state of that sensor, we first set a variable called `birdseen`. To this variable we are assigning the concatenated values of `bird_date_seen`, a single whitespace, and `bird_time_seen`. We're choosing this format because that is the format that `relative_time` returned before when we tried using it without a date. As a quick experiment, take the templating code under the `state: >` parameter above and throw it into Developer Tools > Template. Do you get 126 years? Or something more realistic? If something more realistic, amazing! We're almost there! Here's what you should see in HomeAssistant if the sensor was created correctly. ![Bird Last Seen Entity](../posts/img/birdnet-homeassistant-birdseen-sensor.png) {{< box info >}} If you're new to templating for Home Assistant (or in general!) it would be helpful to read through a few of the docs that HomeAssistant provides. * [HomeAssistant Templating Docs](https://www.home-assistant.io/docs/configuration/templating/) * [Jinja2 Templating Engine Docs](https://palletsprojects.com/p/jinja) _Note: Jinja2 is very popular and common. Learning it for home automation is worth it alone, but it may very well come in handy in other places too!_ {{< /box >}} {{< details "Data Card Yaml" >}} ```yaml type: horizontal-stack cards: - type: custom:button-card entity: sensor.bird_time_seen show_state: true show_icon: true show_name: false icon: mdi:clock-outline color: darkgrey styles: card: - border: none - background: transparent - type: custom:button-card entity: sensor.bird_confidence show_state: true show_icon: true show_name: false icon: mdi:check-circle styles: card: - border: none - background: transparent icon: - color: | [[[ if (states['sensor.bird_confidence'].state > 80 ) return "green"; return "lightblue"; ]]] - type: custom:button-card entity: sensor.bird_last_seen show_state: true show_icon: true show_name: false icon: mdi:timer-refresh-outline styles: card: - border: none - background: transparent icon: - color: | [[[ var y = states['sensor.bird_last_seen'].state; let x = y.slice(0, 2); var e = Number(x); if (e < 5) return '#ff6969'; if (e < 10) return '#ffdf87'; if (e < 15) return '#d9d76f'; if (e < 20) return '#fcc2ea'; else return '#ccccc8'; ]]] ``` {{< /details >}} ## Weather Card ![Weather Card](../posts/img/birdnet-homeassistant-weather-card.png) This doesn't need a lot of explaining or instructions. It is just the standard weather card! Here's the YAML, none of the less, so you know what I toggled on/off. I'm using [Pirate Weather Integration](https://pirateweather.net/en/latest/) as my data source. {{< details "Weather Card" >}} ```yaml type: custom:weather-card entity: weather.pirateweather forecast: false hourly_forecast: false name: null details: true current: true number_of_forecasts: '5' ``` {{< /details >}} ## Description Card Finally, we reach the bottom of the dashboard: the description card. This one is also really straightforward. We're just using a [standard markdown card](https://www.home-assistant.io/dashboards/markdown/) and taking the description sensor we created using Wikipedia's API and making that the main content of the card. ![Bird Description Card](../posts/img/birdnet-homeassistant-description-card.png) Other than setting the theme, the only other small changes are removing the border and increasing from the default font size. We'll use [Thomas Loven's](https://github.com/thomasloven) famous Card Mod for that. {{< details "Description Card">}} ```yaml type: markdown content: '{{ state_attr(''sensor.birdnet_wiki'',''description'')}}' theme: Catppuccin Mocha card_mod: style: | ha-card.type-markdown { border: none; } ha-markdown { font-size: 16px; } ``` {{< /details >}} ## Conclusion And that's all there is to it! I say that flippantly, but I know that it can seem like there's a lot of setup. Everything I did here evolved out of other people's projects and dashboards on [Reddit](www.reddit.com/r/homeassistant) or the invaluable [HomeAssistant Community](https://community.home-assistant.io/) Please feel free to reach out to me on [Mastodon](www.fosstodon.org/@notnorm) if you have any questions or get stuck anywhere! ## Full Dashboard YAML {{< details "Full Dashboard YAML" >}} ```yaml - theme: Catppuccin Macchiato title: BirdNet-Dashboard path: birdnet-dashboard icon: mdi:bird type: custom:vertical-layout badges: [] cards: - type: horizontal-stack cards: - type: custom:button-card entity: sensor.bird_common_name triggers_update: all show_name: false show_icon: false show_state: false show_label: false styles: card: - background: transparent - border: none - width: 215px - height: 175px custom_fields: picture: card: type: custom:button-card entity: camera.birdnet_flickr show_entity_picture: true show_name: false show_icon: false styles: card: - height: 100% - width: 100% - padding: 0px 15px 0px 15px - border-radius: 3px 3px 15px 3px - border: none - background: transparent - overflow: visible img_cell: - width: 180px - height: 160px - border-radius: 69% - border: 3px solid grey entity_picture: - width: 215px - height: 100% - type: vertical-stack cards: - type: custom:button-card entity: sensor.bird_common_name show_entity_picture: true show_state: true show_name: false show_icon: false styles: card: - background: transparent - border: none - margin-top: 35px - font-size: 25px - width: auto - type: custom:button-card entity: sensor.bird_science_name show_entity_picture: true show_state: true show_name: false show_icon: false styles: card: - background: transparent - border: black - width: auto - type: horizontal-stack cards: - type: custom:button-card entity: sensor.bird_time_seen show_state: true show_icon: true show_name: false icon: mdi:clock-outline color: darkgrey styles: card: - border: none - background: transparent - type: custom:button-card entity: sensor.bird_confidence show_state: true show_icon: true show_name: false icon: mdi:check-circle styles: card: - border: none - background: transparent icon: - color: | [[[ if (states['sensor.bird_confidence'].state > 80 ) return "green"; return "lightblue"; ]]] - type: custom:button-card entity: sensor.bird_last_seen show_state: true show_icon: true show_name: false icon: mdi:timer-refresh-outline styles: card: - border: none - background: transparent icon: - color: | [[[ var y = states['sensor.bird_last_seen'].state; let x = y.slice(0, 2); var e = Number(x); if (e < 5) return '#ff6969'; if (e < 10) return '#ffdf87'; if (e < 15) return '#d9d76f'; if (e < 20) return '#fcc2ea'; else return '#ccccc8'; ]]] - type: custom:weather-card entity: weather.pirateweather forecast: false hourly_forecast: false name: null details: true current: true - type: markdown content: '{{ state_attr(''sensor.birdnet_wiki'',''description'')}}' theme: Catppuccin Mocha card_mod: style: | ha-card.type-markdown { border: none; } ha-markdown { font-size: 16px; } ``` {{< /details >}}