Checking for Entities

If you’re following up on this from my first post, 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?!

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.

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

I’ve included the code for all the cards at the bottom of this post. You can find them here. 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:
    • 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:

Now that we’ve got that squared away, let’s jump into each card.

Overview Card

Overview Card

You’ll notice from the few dependencies listed above, that I use this button card. A lot. 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!

Overview Card 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

Data Card

Data Card

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.

{{ now() - state_attr(sensor.bird_time_seen, 'last_triggered') > timedelta(hours=24) }}

or:

{% 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.

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.

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:

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

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.

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!

Data Card 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';
            ]]]            

Weather Card

Weather Card

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 as my data source.

Weather Card
type: custom:weather-card
entity: weather.pirateweather
forecast: false
hourly_forecast: false
name: null
details: true
current: true
number_of_forecasts: '5'

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 and taking the description sensor we created using Wikipedia’s API and making that the main content of the card.

Bird Description Card

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 famous Card Mod for that.

Description Card
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;
    }    

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 or the invaluable HomeAssistant Community

Please feel free to reach out to me on Mastodon if you have any questions or get stuck anywhere!

Full Dashboard YAML

Full Dashboard 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;
            }