Getting Started with NINA Integration

Although this thread is a little older, someone may be interested in my realization of NINA.

I made the NINA visualization for the available integration with a markdown card.
With this code I solved the problem with 5 sensors. They are all displayed in one card with a loop, separated by a horizontal line. The headlines are colorized according to the warning level.

If you like to ignore messages from dedicated sources like DWD you can add a variable which contains the attribute “sender” and a conditional line in the loop.

Instead of xxx in the code below fill in your selected sensor name without the index in the last character. The index is added from loop.index. The sensor name can be found in the developers section of HA.
Testing is possible if you do not block the covid messages in the integration. But of course you can choose a station with actual messages to test it, too.

Feel free to optimize it your way - and let me know :wink:

type: markdown
content: |-
  {% set ns = namespace() %}
  <img src="/local/Nina_app.png" alt="Nina" width="45"/>
  {% set ns.msg = false %}
  {% for i in  range(5) %}
    {% set status = states("binary_sensor.warning_xxx_" ~ loop.index) %}
      {% if(status == "on") %}
  ***
        {% set ns.msg = true %}     
        {% set headline = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "headline")%}
        {% set description = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "description") %} 
        {% set level = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "severity")%}
        {% set sent = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "sent")%}
        {% set start = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "start")%}
        {% set expire = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "expires")%}
  <font color=#000000>{{ sent }}</font>
        {% if level == "Extreme" %}
  **<font color=#ff0000>{{ headline }}</font>**
        {% elif level == "Severe" %}
  **<font color=#ffA500>{{ headline }}</font>**
        {% elif level == "Moderate" %}
  **<font color=#F6BE00>{{ headline }}</font>**
        {% elif level == "Minor" %}    
  **<font color=#0000ff>{{ headline }}</font>**
        {% else %}
  **<font color=#000000>{{ headline }}</font>**
        {% endif %}
  <font color=#000000>{{ description | trim }}</font>
        {% if start %}
  *<font color=#666666>Begin: {{ start }}</font>*
        {% endif %}
        {% if expire %}
  *<font color=#666666>Expires: {{ expire }}</font>*
        {% endif %}
      {% endif %}
  {% endfor %}
  {% if ns.msg == false %}
  *<font color=#000000>No actual warnings.</font>*
  {% endif %}
title: Nina

I resused my markdown that I also use for DWD (I actually will be fitlering the NINA data that comes from DWD out) to get this (note duplicate NINA event to verify the horizontal line logic)

{% for i in  range(5) %}
  {% set status = states("binary_sensor.warning_karlsruhe_stadt_" ~ loop.index) %}
	{% if(status == "on") %} 
	  {% if not loop.first %}
***
	  {% endif %}
	  {% if state_attr("binary_sensor.warning_xxx_" ~ loop.index, "id").startswith("dwd.") %}
<img src="/local/images/nina_app.png" width="24"/> *Siehe Deutscher
Wetterdienst*
	  {% else %}
		{% set headline = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "headline") %}
		{% set description = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "description") %} 
		{% set level = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "severity") %}
		{% set time_sent = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "sent") | as_timestamp(0) %}
		{% set weekday_sent = time_sent | timestamp_custom("%w", True) | int %}
		{% set time_start = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "start") %}
		{% if time_start != '' %}{% set weekday_start = time_start | as_timestamp | timestamp_custom("%w", True) | int %}{% endif %}
		{% set time_end = state_attr("binary_sensor.warning_xxx_" ~ loop.index, "expires") %}
		{% if time_end != '' %}{% set weekday_end = time_end | as_timestamp | timestamp_custom("%w", True) | int %}{% endif %}
<img src="/local/images/nina_app.png" width="24"/> {% if level ==
"Extreme" %}**<font color=#ff0000>{{ headline }}</font>**
		{% elif level == "Severe" %}**<font color=#ffA500>{{ headline }}</font>**
		{% elif level == "Moderate" %}**<font color=#F6BE00>{{ headline }}</font>**
		{% elif level == "Minor" %}**<font color=#55dd88>{{ headline }}</font>**
		{% else %}{{ headline }}
		{% endif %}*<font color=gray>{% if time_start != '' %}
		  {{['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_start-1] ~ " " ~ time_start | as_timestamp | timestamp_custom("%H:%M") }}{% else %}Unbekannt{% endif %} - {% if time_end != '' %}{{ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_end-1] ~ " " ~ time_end | as_timestamp | timestamp_custom("%H:%M")}}{% else %}Unbekannt{% endif %}</font>*
		{{ description | trim }}
	  {% endif %}
	{% endif %}
{% endfor %}

Icon / image alignment is not perfect yet…

1 Like

Hi,
I really like the NINA integration for HA.

However, I have a small issue, and I was wondering if anyone has an idea how to resolve this:
I live in a small village called “Prisdorf”, which is part of the “Landkreis Pinneberg”. When I select Prisdorf in the NINA integration, I receive warning messages for the whole Landkreis Pinneberg.
That’s kind of an issue, as Helgoland (an island in the North Sea, about 130km away from Prisdorf) is part of the same Landkreis, but their storm warnings are not that relevant to me…

In the official NINA App I can select if I want to receive just the messages for my village, or for the whole Landkreis.
I haven’t found a way yet to make a similar distinction in the NINA integration for HA.

I would be grateful if anyone has an idea how to resolve this, or if I just missed a setting or configuration somewhere.

Many thanks!

Does the Nina Mobile App provide such a granularity feature?
Consider using DWD for storm warnings, and filter NINA storm warnings.

Thanks for your suggestion, Heinrich!

Yes, the Nina Mobile App indeed has such a feature. There is a slider in the “Warngebiet bearbeiten” dialogue, where you can chose between a 1 km2 or 9 km2 circle around a location, or alternatively the geographical borders of either “Gemeinde” or “Landkreis”.
The latter would be a great addition to the Nina Integration.

For now I will follow your suggestion and filter the warnings.

Thanks again!

In case someone is using my markdown template I posted last December (Getting Started with NINA Integration - #22 by spacegaier): I just updated that because it was not able to gracefully handle NINA entries that do not have a start and end time set.

I just had this scenario with a potential upcoming bomb disposal warning. The template will now print “Unbekannt” if no time is provided for one of those data points.

image

wow, that is some weather alert… never seen that …

nice integration I hadn’t seen before. Since one of my children is moving to the German border I figurer it to be a good addition to my alerts system :wink:

had a bit of an initial struggle but got things going for a few relevant cities now. Because I always dislike to hardcode those, I use auto-entities (filter integration) a lot, but for starters use this for loop in Markdown directly:

  - type: conditional
    conditions:
      - entity: binary_sensor.nina_warnings_active
        state: 'on'
    card:
      type: markdown
      content: >
        <img src="/local/images/nina.png" width="24"/>
        {% for w in expand(integration_entities('nina'))
          if w.state == 'on' %}
        ## Be aware: {{w.attributes.friendly_name}}
        > Headline: {{w.attributes.headline}}
        > Description: {{w.attributes.description}}
        <br>
        > Severity: *{{w.attributes.severity}}*
        > From *{{w.attributes.start}}* to *{{w.attributes.expires}}*
        > Sender: {{w.attributes.sender}}
        {% endfor %}

Given the fact the Nina integration does not provide a binary Ive made that quickly so we can use it as conditional in the frontend:

  - binary_sensor:

      - unique_id: nina_warnings_active
        state: >
          {{integration_entities('nina')
            |reject('eq','')
            |map('states')
            |select('eq','on')
            |list|count}}
        device_class: problem

nothing fancy just yet, and coming here for inspiration.
most of all Id love to see if I can build that attributes list less verbosely in the loop, and maybe some extra formatting on the timings.

have to throw some warning first :wink:

update

after some Nina alerts ive moved to:

type: conditional
conditions:
  - entity: binary_sensor.nina_warnings_active
    state: 'on'
card:
  type: entities
  title: Nina waarschuwingen aktief
  card_mod:
    class: class-header-margin-no-color
    style: |
      .card-header {
        color: rgb(231,92,9);
        background: rgb(18,46,129);
      }
  entities:
    - type: custom:hui-element
      card_type: markdown
      card_mod:
        style: |
          ha-card {
            margin: 0px -16px -16px -16px;
            box-shadow: none;
          }
      content: >
        {% for w in expand(integration_entities('nina'))
          if w.state == 'on' %}
        {% set severity = w.attributes.severity %}
        {% set colors = {'Extreme':'#ff0000','Moderate':'#F6BE00','Minor':'#55dd88','Severe':'#800000'} %}
        {% set fontcolor = colors.get(severity,'grey') %}
          <font color = {{fontcolor}}>**{{w.attributes.friendly_name}}**</font>

          > **Headline:** <font color = {{fontcolor}}>{{w.attributes.headline}}</font>
          > **Description:** {{w.attributes.description}}
          > **Severity:** <font color = {{fontcolor}}>*{{severity}}*</font>
          {% if w.attributes.start and w.attributes.expires -%}
          > From: *{{as_timestamp(w.attributes.start)|timestamp_custom('%a %-d %b %H:%M')}}* to *{{as_timestamp(w.attributes.expires)|timestamp_custom('%a %-d %b %H:%M')}}*
          {%- endif %}
          {% if w.attributes.sender -%}
          > Sender: {{w.attributes.sender}}
          {%- endif %}
        {% if not loop.last %} {{'___'}} {% endif %}
        {% endfor %}

        <img src="/local/images/mowas.png" width="35"> [Live Warnmeldungen übersicht](https://warnung.bund.de/meldungen)

cant get the icon to behave nicely with core Markdown ###, so had to fallback to using text and finally decided to have the one line with a bad line at the bottom…

might be able to align it a bit better , but havent have the time yet. for now this seems to be adequate

any vertical-along img html tag seems to not work…

1 Like

now this is special:

be careful! hati-hati…

Thank you guys for sharing your designs and code!

Now that I’m done with my setup for NINA, I wanted to share my dashbaord cards - maybe some of you are interested in how I display the warnings. :slight_smile:

The card needs only a binary_sensor helper (type: group) to indicate whether there is one or more warning active and the individual warning sensors. **No other integrations (except NINA) are needed**. I only use the integrated ha-alert markdown to list the individual warnings .

Features:

  • Only displays active warnings and disappears completely when there are no warnings
  • Built in checks to see whether the used attributes are neither empty nor missing
    → see {# --- Check attribute content --- #} -block
  • Settings block at the beginning to easily change appearance
  • Some comments

Missing Features:

  • Dismissable Warnings / Slotted Action
    → I was unable to get the dismissable tag working see: design.home-assistant.io/#components/ha-alert
  • Expandable Description
    → while truncating the description after a certain number of characters is easy (e.g. "veryLongString"|truncate(9, True)), I have no idea how to implement something that expands

Your code and the following links helped me quite a lot:
See post below for the links to the other usefull resources. (new user link limit)

In the preview you can see severity: Severe, Extreme, Minor, Moderate. The picture on the right has show_source = true

The code for this card can be found here:

5 Likes

The very usefull resources:
Jinja2 Template Documentation
Commonmark Markdown Documentation

Thank you for the code. I use it, but I can see the card only on the design screen but not on the final dashboard. What do I have to change?

Hey wildland,

i love your card, it looks really nice. For some reason it doesn’t show anything in my companion app on the tablet the first time the page is loaded (if i do a refresh it shows fine, but thats no option for me to use every time), i nailed it down and it seems the entity filter card seems to be the problem, maybe it stumbles over the layout or custom mod card i use.
So the question, is there a way to use your templated code without the filter card and give the entites of nina in the template of the markdown card?

I don’t know why, but it does not work with state:‘on’, but it works with state_not:‘off’.

How i can show only warnings higher than “Minor”?

is there a way to get NINA Warnings by GEOLOCATION (GPS-Data)? My “Home” is mobile and I’d like to recieve the NINA Warnings related to my actual position.

There must be, because the NINA app for Android is capable of checking events near your current location.
However, there is no official way to use geocoords to poll the NINA API, so I guess there is an inofficial way to translate geo coordinates into location IDs (which NINA uses).

You might want to ask the developers about that: [email protected]

i have modified it a little bit.
maybe someone can config it so that i can filter which severity i wanna display

{% set ns = namespace() %}
{% set ns.msg = false %}
{% set weekdays = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] %}
{% set months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"] %}
{% for i in  range(5) %}
  {% set status = states("binary_sensor.warning_landshut_" ~ loop.index) %}
    {% if(status == "on") %}
      {% set ns.msg = true %}     
      {% set headline = state_attr("binary_sensor.warning_landshut_" ~ loop.index, "headline")%}
      {% set description = state_attr("binary_sensor.warning_landshut_" ~ loop.index, "description") %} 
      {% set level = state_attr("binary_sensor.warning_landshut_" ~ loop.index, "severity")%}
      {% set sent = state_attr("binary_sensor.warning_landshut_" ~ loop.index, "sent")%}
      {% set start = state_attr("binary_sensor.warning_landshut_" ~ loop.index, "start")%}
      {% set start_date = start | as_datetime %}
      {% set start_weekday = start_date.strftime('%A') %}
      {% set start_time = start_date.strftime('%H:%M') %}
      {% set expire = state_attr("binary_sensor.warning_landshut_" ~ loop.index, "expires")%}
      {% set expire_date = expire | as_datetime %}
      {% set expire_weekday = expire_date.strftime('%A') %}
      {% set expire_time = expire_date.strftime('%H:%M') %}
      {% if level == "Extreme" %}
**<font color=#ff0000><ha-alert alert-type="error">{{ headline }}</font>**
      {% elif level == "Severe" %}
**<font color=#ffA500><ha-alert alert-type="warning">{{ headline }}</font>**
      {% elif level == "Moderate" %}
**<font color=#F6BE00><ha-alert alert-type="info">{{ headline }}</font>**
      {% elif level == "Minor" %}    
**<font color=#0000ff><ha-alert alert-type="info">{{ headline }}</font>**
      {% else %}
**<font color=#000000>{{ headline }}</font>**
      {% endif %}
      {% if start %}
<b><font color=#666666>{{ start_weekday }} {{ start_time }} - {{ expire_weekday }} {{ expire_time }}</font></b>
      {% endif %}
<font color=white>{{ description | trim }}</font>
***
    {% endif %}
{% endfor %}
{% if ns.msg == false %}
*<font color=#000000>No actual warnings.</font>*
{% endif %}

Finally took there time to take the warnings and translate the dates/timings into my own Language…

Use Easy_time by Petro and create the date times easily…
some extra formatting and conditional displaying of attributes makes this work very well.
Note I stopped using the expand(), and now only take the attributes at a time from the integration_entities(), hope this makes it a bit more efficient. I try to avoid generic expand where I can

type: entities
visibility:
  - condition: state
    entity: binary_sensor.nina_warnings_active
    state: 'on'
title: Nina waarschuwingen aktief
card_mod:
  class: class-header-margin-no-color
  style: |
    .card-header {
      color: rgb(231,92,9);
      background: rgb(18,46,129);
    }
entities:
  - type: custom:hui-element
    card_type: markdown
    card_mod:
      style: |
        ha-card.type-markdown {
          margin: 0px -16px -16px -16px;
          box-shadow: none;
          overflow-y: scroll;
          max-height: 500px;
        }

    content: >

      {% for w in integration_entities('nina')|select('is_state','on') %}
      {% set severity = state_attr(w,'severity') %}
      {% set colors =
        {'Extreme':'#ff0000','Moderate':'#F6BE00','Minor':'#55dd88','Severe':'#800000'} %}
      {% set fontcolor = colors.get(severity,'#e75c09') %}
      {% set gepubliceerd = state_attr(w,'sent')|as_datetime|as_local %}
      {% set van = state_attr(w,'start')|as_datetime|as_local %}
      {% set tot = state_attr(w,'expires')|as_datetime|as_local %}
      {% set area = state_attr(w,'affected_areas') %}
      {% set advies = state_attr(w,'recommended_actions') %}
      {% from 'easy_time.jinja' import weekday, month %}
        <b><font color = {{fontcolor}}>{{state_attr(w,'friendly_name')}}</font></b>
        <b>Titel:</b> <font color = {{fontcolor}}>{{state_attr(w,'headline')}}</font>
        <b>Waarschuwing:</b>
        {{state_attr(w,'description')}}
        <b>Gebied:</b> {{area}}
        <b>Schaal:</b> <font color = {{fontcolor}}>*{{severity}}*</font>
        {% if advies %}
        <b>Advies:</b> {{advies}}
        {% endif %}
        {% if state_attr(w,'start') and state_attr(w,'expires') -%}
        <b>Geldig:</b>
        <i>van:</i> {{ weekday(van)}} {{van.day}} {{month(van)}} {{van.strftime('%H:%M')}}
        <i>tot:</i> {{weekday(tot)}} {{tot.day}} {{month(tot)}} {{tot.strftime('%H:%M')}}
        {%- endif %}
        <b>Gepubliceerd:</b> {{weekday(gepubliceerd) ~' '~gepubliceerd.day~' '~month(gepubliceerd)~' om '~gepubliceerd.strftime('%H:%M') }}

        {% if state_attr(w,'sender') -%}
        <b>Bron:</b> {{state_attr(w,'sender')}}
        {%- endif %}
      {% if not loop.last %} {{'___'}} {% endif %}
      {% endfor %}

#       {% for w in expand(integration_entities('nina')|select('is_state','on')) %}
#       {% set severity = w.attributes.severity %}
#       {% set colors = {'Extreme':'#ff0000','Moderate':'#F6BE00','Minor':'#55dd88','Severe':'#800000'} %}
#       {% set fontcolor = colors.get(severity,'gray') %}
#         <font color = {{fontcolor}}>**{{w.attributes.friendly_name}}**</font>
#
#         > **Headline:** <font color = {{fontcolor}}>{{w.attributes.headline}}</font>
#         > **Description:** {{w.attributes.description}}
#         > **Severity:** <font color = {{fontcolor}}>*{{severity}}*</font>
#         {% if w.attributes.start and w.attributes.expires -%}
#         > From: *{{as_timestamp(w.attributes.start)|timestamp_custom('%a %-d %b %H:%M')}}* to *{{as_timestamp(w.attributes.expires)|timestamp_custom('%a %-d %b %H:%M')}}*
#         {%- endif %}
#         {% if w.attributes.sender -%}
#         > Sender: {{w.attributes.sender}}
#         {%- endif %}
#       {% if not loop.last %} {{'___'}} {% endif %}
#       {% endfor %}

  - type: custom:hui-element
    card_type: markdown
    card_mod:
      style: |
        ha-card.type-markdown {
          margin: 0px -16px -16px -16px;
          box-shadow: none;
          border-top: 2px groove var(--divider-color);
        }

    content: >
      <img src="/local/images/mowas.png" width="35"> [Live Warnmeldungen übersicht](https://warnung.bund.de/meldungen)