Amateur Radio HF Propagation Conditions

Summary

I was after making my own dashboard to monitor HF conditions for amateur/ham radio, using the excellent data and services provide by N0NBH’s HF Propagation Tools and Solar Data and KC2G for ionospheric conditions. Screenshot below:

Details

Sensors

N0NBH provides a XML output of the data, which can be polled hourly. The multiscrape custom component is ideal for this, with the following configuration to pick out key things I was interested in.

multiscrape:
  resource: https://www.hamqsl.com/solarxml.php
  scan_interval: 3600
  sensor:
    - unique_id: solar_flux_index
      name: "Solar Flux Index"
      select: "solarflux"
      value_template: '{{ value.strip()|int }}'
      state_class: measurement
      icon: mdi:weather-sunny-alert
    - unique_id: solar_sunspots
      name: "Solar Sunspots"
      select: "sunspots"
      value_template: '{{ value.strip()|int }}'
      state_class: measurement
      icon: mdi:weather-sunny-alert
    - unique_id: solar_a_index
      name: "Solar A-Index"
      select: "aindex"
      value_template: '{{ value.strip()|int }}'
      state_class: measurement
      icon: mdi:alpha-a
    - unique_id: solar_k_index
      name: "Solar K-Index"
      select: "kindex"
      value_template: '{{ value.strip()|int }}'
      state_class: measurement
      icon: mdi:alpha-k
    - unique_id: solar_bz
      name: "Solar Bz"
      select: "magneticfield"
      value_template: '{{ value.strip()|float }}'
      state_class: measurement
      icon: mdi:sun-compass
    - unique_id: solar_wind
      name: "Solar Wind"
      select: "solarwind"
      value_template: '{{ value.strip()|float }}'
      state_class: measurement
      icon: mdi:sun-wireless
      unit_of_measurement: km/s
    - unique_id: solar_hf_80_40_day
      name: "HF Conditions 80m-40m Day"
      select: 'band[name="80m-40m"][time="day"]'
      icon: mdi:sine-wave
    - unique_id: solar_hf_30_20_day
      name: "HF Conditions 30m-20m Day"
      select: 'band[name="30m-20m"][time="day"]'
      icon: mdi:sine-wave
    - unique_id: solar_hf_17_15_day
      name: "HF Conditions 17m-15m Day"
      select: 'band[name="17m-15m"][time="day"]'
      icon: mdi:sine-wave
    - unique_id: solar_hf_12_10_day
      name: "HF Conditions 12m-10m Day"
      select: 'band[name="12m-10m"][time="day"]'
      icon: mdi:sine-wave
    - unique_id: solar_hf_80_40_night
      name: "HF Conditions 80m-40m Night"
      select: 'band[name="80m-40m"][time="night"]'
      icon: mdi:sine-wave
    - unique_id: solar_hf_30_20_night
      name: "HF Conditions 30m-20m Night"
      select: 'band[name="30m-20m"][time="night"]'
      icon: mdi:sine-wave
    - unique_id: solar_hf_17_15_night
      name: "HF Conditions 17m-15m Night"
      select: 'band[name="17m-15m"][time="night"]'
      icon: mdi:sine-wave
    - unique_id: solar_hf_12_10_night
      name: "HF Conditions 12m-10m Night"
      select: 'band[name="12m-10m"][time="night"]'
      icon: mdi:sine-wave
    - unique_id: solar_geomag_field
      name: "HF Conditions Geomag Field"
      select: 'geomagfield'
      icon: mdi:compass
    - unique_id: solar_sig_noise_lvl
      name: "HF Conditions Noise Level"
      select: 'signalnoise'
      icon: mdi:signal-cellular-2
      state_class: measurement
      value_template: '{{ value.strip()[1] | int }}'

For fetching local maximum usable frequency (MUF) from KC2G’s site, I used the standard rest sensor, with JSON station data resource. Note you will need to inspect the JSON file per resource below, and replace the ????? with the station code closest to you.

sensor:
  - platform: rest
    name: "Solar HF MUF"
    resource: 'https://prop.kc2g.com/api/stations.json'
    value_template: '{{ (value_json | selectattr("station.code", "==", "?????") | list | first).mufd | float(default=float("NaN")) }}'
    state_class: measurement
    scan_interval: 600
    unit_of_measurement: MHz

Dashboard

For this I used great lovelace addon mini-graph-card. Obviously you may want to tweak the colours, layout, etc. to suit your needs.

cards:
  - square: false
    columns: 2
    type: grid
    cards:
      - type: custom:mini-graph-card
        name: Solar Flux Index
        font_size: 75
        hour24: true
        hours_to_show: 48
        upper_bound: 250
        lower_bound: 50
        min_bound_range: 200
        show:
          icon: true
        entities:
          - entity: sensor.solar_flux_index
            state_adaptive_color: true
        color_thresholds:
          - value: 80
            color: '#ff0000'
          - '#ffbf00'
          - value: 150
            color: '#00ff00'
      - type: custom:mini-graph-card
        name: Sunspots
        font_size: 75
        hours_to_show: 48
        hour24: true
        upper_bound: 200
        lower_bound: 20
        min_bound_range: 200
        show:
          icon: true
        entities:
          - entity: sensor.solar_sunspots
            state_adaptive_color: true
        color_thresholds:
          - value: 20
            color: '#ff0000'
          - '#ffbf00'
          - value: 100
            color: '#00ff00'
      - type: custom:mini-graph-card
        name: A-Index
        font_size: 75
        hour24: true
        hours_to_show: 48
        upper_bound: 50
        lower_bound: 0
        min_bound_range: 30
        show:
          icon: true
        entities:
          - entity: sensor.solar_a_index
            state_adaptive_color: true
        color_thresholds:
          - value: 50
            color: '#ff0000'
          - '#ffbf00'
          - value: 0
            color: '#00ff00'
      - type: custom:mini-graph-card
        name: K-Index
        font_size: 75
        hours_to_show: 48
        hour24: true
        upper_bound: 9
        lower_bound: 0
        min_bound_range: 9
        show:
          icon: true
        entities:
          - entity: sensor.solar_k_index
            state_adaptive_color: true
        color_thresholds:
          - value: 8
            color: '#ff0000'
          - value: 4
            color: '#ffbf00'
          - value: 1
            color: '#00ff00'
      - type: custom:mini-graph-card
        name: Bz
        font_size: 75
        hour24: true
        hours_to_show: 48
        upper_bound: 20
        lower_bound: -20
        min_bound_range: 40
        show:
          icon: true
        entities:
          - entity: sensor.solar_bz
            state_adaptive_color: true
        color_thresholds:
          - value: -20
            color: '#ff0000'
          - '#ffbf00'
          - value: 0
            color: '#00ff00'
          - '#ffbf00'
          - value: 20
            color: '#00ff00'
      - type: custom:mini-graph-card
        name: Solar Wind
        font_size: 75
        hours_to_show: 48
        hour24: true
        upper_bound: 700
        lower_bound: 200
        min_bound_range: 500
        show:
          icon: true
        entities:
          - entity: sensor.solar_wind
            state_adaptive_color: true
        color_thresholds:
          - value: 700
            color: '#ff0000'
          - '#ffbf00'
          - value: 300
            color: '#00ff00'
  - square: false
    columns: 2
    type: grid
    cards:
      - type: custom:mini-graph-card
        name: MUF
        font_size: 75
        hours_to_show: 48
        hour24: true
        upper_bound: 30
        lower_bound: 7
        min_bound_range: 23
        show:
          labels: hover
        entities:
          - entity: sensor.solar_hf_muf
            state_adaptive_color: true
        color_thresholds:
          - value: 3
            color: '#ff0000'
          - value: 14
            color: '#ffbf00'
          - value: 28
            color: '#00ff00'
      - type: custom:mini-graph-card
        name: Noise
        font_size: 75
        unit: S-units
        hours_to_show: 48
        hour24: true
        upper_bound: 9
        lower_bound: 0
        min_bound_range: 7
        show:
          labels: hover
        entities:
          - entity: sensor.solar_sig_noise_lvl
            state_adaptive_color: true
        color_thresholds:
          - value: 6
            color: '#ff0000'
          - '#ffbf00'
          - value: 1
            color: '#00ff00'
      - type: custom:mini-graph-card
        font_size: 75
        name: 80m - 40m
        hours_to_show: 48
        hour24: true
        upper_bound: 2
        lower_bound: 0
        min_bound_range: 2
        show:
          legend: false
        entities:
          - entity: sensor.solar_hf_80_40_day
            name: Day
            show_state: true
            state_adaptive_color: true
          - entity: sensor.solar_hf_80_40_night
            name: Night
            show_state: true
            state_adaptive_color: true
        state_map:
          - value: Poor
            label: Poor
          - value: Fair
            label: Fair
          - value: Good
            label: Good
        smoothing: false
      - type: custom:mini-graph-card
        font_size: 75
        name: 30m - 20m
        hours_to_show: 48
        hour24: true
        upper_bound: 2
        lower_bound: 0
        min_bound_range: 2
        show:
          legend: false
        entities:
          - entity: sensor.solar_hf_30_20_day
            name: Day
            show_state: true
            state_adaptive_color: true
          - entity: sensor.solar_hf_30_20_night
            name: Night
            show_state: true
            state_adaptive_color: true
        state_map:
          - value: Poor
            label: Poor
          - value: Fair
            label: Fair
          - value: Good
            label: Good
        smoothing: false
      - type: custom:mini-graph-card
        font_size: 75
        name: 17m - 15m
        hours_to_show: 48
        hour24: true
        upper_bound: 2
        lower_bound: 0
        min_bound_range: 2
        show:
          legend: false
        entities:
          - entity: sensor.solar_hf_17_15_day
            name: Day
            show_state: true
            state_adaptive_color: true
          - entity: sensor.solar_hf_17_15_night
            name: Night
            show_state: true
            state_adaptive_color: true
        state_map:
          - value: Poor
            label: Poor
          - value: Fair
            label: Fair
          - value: Good
            label: Good
        smoothing: false
      - type: custom:mini-graph-card
        font_size: 75
        name: 12m - 10m
        hours_to_show: 48
        hour24: true
        upper_bound: 2
        lower_bound: 0
        min_bound_range: 2
        show:
          legend: false
        entities:
          - entity: sensor.solar_hf_12_10_day
            name: Day
            show_state: true
            state_adaptive_color: true
          - entity: sensor.solar_hf_12_10_night
            name: Night
            show_state: true
            state_adaptive_color: true
        state_map:
          - value: Poor
            label: Poor
          - value: Fair
            label: Fair
          - value: Good
            label: Good
        smoothing: false
6 Likes

Thank you so much for this solution!
I was trying to create similair cards but got stuck
all the time.

1 Like

Thanks @Jan66. Glad you’ve found it useful. I’d welcome any suggestions. I’ve found that the statistics graphs can be use to produce some interesting plots.

i m getting this error: Configuration invalid!

                Invalid config for [sensor.rest]: invalid template (TemplateSyntaxError: unexpected char '?' at 14) for dictionary value @ data['value_template']. Got '{{ value_json[?].mufd | float(default=float("NaN")) }}'. (See ?, line ?).

@rancho You’ll need to replace the ? with the index for the station nearest you. If you open the JSON link in your browser, it should display them numbered appropriately.

1 Like

thanks all good now

Hi, I’m KC2G. I don’t recommend accessing stations.json by index — there’s no guarantee that a given ionosonde will stay at the same position in that array over time. Perhaps something like {{ value_json | selectattr('station.code', '==', 'MHJ45') | list | first | attr('mufd') }} to fetch Millstone? (Forgive me if that doesn’t work, I’m not a HomeAssistant user and I’m not really familiar with Jinja, but hopefully someone can see what I meant and patch it up).

1 Like

Alternatively, I could add a new API endpoint that automatically selects the nearest ionosonde to a given latitude/longitude (and also filters based on maximum measurement age and minimum confidence score). Would that appeal? Is there some standard format you would prefer to pass the lat/lon in?

Thanks @arodland for your comments. I was concerned about this originally, but couldn’t quite get the right Jinja template to work.

Your suggestion with a minor tweak of not using attr worked great. I’ve updated my original post with this: {{ (value_json | selectattr("station.code", "==", "MHJ45") | list | first).mufd | float(default=float("NaN")) }}

Also a big thanks for hosting, and sharing the code for, your excellent propagation maps/data :grinning_face_with_smiling_eyes:

1 Like

Hi, great work.
I’m not a programmer skilled home assistant user, is the upper code updated with the latest code that choose the nearest ionosonde to my location?
Will try to get it working…
73

I managed to get the entities working but didn’t managed to get the min-graph working…

Any idea?

Try adding
type: vertical-stack

at the top before cards:. I had the same issue.

1 Like

It was a question of indentation manage to fix it.

Which files do I edit with the above configurations? I have my configuration.yaml split out and don’t put anything in it other than files / directories.

Where do I put the 2 ‘configs’? I’m not worried about the custom card, I can handle that.

Thank you.

The multiscrape can go in main configuration.yaml or whichever split out file makes sense. The rest sensor for Solar HF MUF needs to go with any other sensors you may have defined.

1 Like

Hello everyone, I tried to facilitate the use of sensors for radio propagation.
I created a custom component that allows you to quickly have all the information.

You can install it via HACS Let me know your opinions and any suggestions.
Thank you
73 de IZ0IJD

2 Likes

Excellent. Thanks for creating the addon @emics Works great. I’ve now replaced the multiscrape addon I was using.

Updated lovelace cards with new sensor names to make it easier for others to copy and paste:

    cards:
      - square: false
        columns: 2
        type: grid
        cards:
          - type: custom:mini-graph-card
            name: Solar Flux Index
            font_size: 75
            hour24: true
            hours_to_show: 48
            upper_bound: 250
            lower_bound: 50
            min_bound_range: 200
            show:
              icon: true
            entities:
              - entity: sensor.ham_radio_propagation_solar_flux_index
                state_adaptive_color: true
            color_thresholds:
              - value: 80
                color: '#ff0000'
              - '#ffbf00'
              - value: 150
                color: '#00ff00'
          - type: custom:mini-graph-card
            name: Sunspots
            font_size: 75
            hours_to_show: 48
            hour24: true
            upper_bound: 200
            lower_bound: 20
            min_bound_range: 200
            show:
              icon: true
            entities:
              - entity: sensor.ham_radio_propagation_solar_sunspots
                state_adaptive_color: true
            color_thresholds:
              - value: 20
                color: '#ff0000'
              - '#ffbf00'
              - value: 100
                color: '#00ff00'
          - type: custom:mini-graph-card
            name: A-Index
            font_size: 75
            hour24: true
            hours_to_show: 48
            upper_bound: 50
            lower_bound: 0
            min_bound_range: 30
            show:
              icon: true
            entities:
              - entity: sensor.ham_radio_propagation_solar_a_index
                state_adaptive_color: true
            color_thresholds:
              - value: 50
                color: '#ff0000'
              - '#ffbf00'
              - value: 0
                color: '#00ff00'
          - type: custom:mini-graph-card
            name: K-Index
            font_size: 75
            hours_to_show: 48
            hour24: true
            upper_bound: 9
            lower_bound: 0
            min_bound_range: 9
            show:
              icon: true
            entities:
              - entity: sensor.ham_radio_propagation_solar_k_index
                state_adaptive_color: true
            color_thresholds:
              - value: 8
                color: '#ff0000'
              - value: 4
                color: '#ffbf00'
              - value: 1
                color: '#00ff00'
          - type: custom:mini-graph-card
            name: Bz
            font_size: 75
            hour24: true
            hours_to_show: 48
            upper_bound: 20
            lower_bound: -20
            min_bound_range: 40
            show:
              icon: true
            entities:
              - entity: sensor.ham_radio_propagation_solar_bz
                state_adaptive_color: true
            color_thresholds:
              - value: -20
                color: '#ff0000'
              - '#ffbf00'
              - value: 0
                color: '#00ff00'
              - '#ffbf00'
              - value: 20
                color: '#00ff00'
          - type: custom:mini-graph-card
            name: Solar Wind
            font_size: 75
            hours_to_show: 48
            hour24: true
            upper_bound: 700
            lower_bound: 200
            min_bound_range: 500
            show:
              icon: true
            entities:
              - entity: sensor.ham_radio_propagation_solar_wind
                state_adaptive_color: true
            color_thresholds:
              - value: 700
                color: '#ff0000'
              - '#ffbf00'
              - value: 300
                color: '#00ff00'
      - square: false
        columns: 2
        type: grid
        cards:
          - type: custom:mini-graph-card
            name: MUF
            font_size: 75
            hours_to_show: 48
            hour24: true
            upper_bound: 30
            lower_bound: 7
            min_bound_range: 23
            show:
              labels: hover
            entities:
              - entity: sensor.solar_hf_muf
                state_adaptive_color: true
            color_thresholds:
              - value: 3
                color: '#ff0000'
              - value: 14
                color: '#ffbf00'
              - value: 28
                color: '#00ff00'
          - type: custom:mini-graph-card
            name: Noise
            font_size: 75
            unit: S-units
            hours_to_show: 48
            hour24: true
            upper_bound: 9
            lower_bound: 0
            min_bound_range: 7
            show:
              labels: hover
            entities:
              - entity: sensor.ham_radio_propagation_solar_sig_noise_lvl
                state_adaptive_color: true
            color_thresholds:
              - value: 6
                color: '#ff0000'
              - '#ffbf00'
              - value: 1
                color: '#00ff00'
          - type: custom:mini-graph-card
            font_size: 75
            name: 80m - 40m
            hours_to_show: 48
            hour24: true
            upper_bound: 2
            lower_bound: 0
            min_bound_range: 2
            show:
              legend: false
            entities:
              - entity: sensor.ham_radio_propagation_solar_hf_80_40_day
                name: Day
                show_state: true
                state_adaptive_color: true
              - entity: sensor.ham_radio_propagation_solar_hf_80_40_night
                name: Night
                show_state: true
                state_adaptive_color: true
            state_map:
              - value: Poor
                label: Poor
              - value: Fair
                label: Fair
              - value: Good
                label: Good
            smoothing: false
          - type: custom:mini-graph-card
            font_size: 75
            name: 30m - 20m
            hours_to_show: 48
            hour24: true
            upper_bound: 2
            lower_bound: 0
            min_bound_range: 2
            show:
              legend: false
            entities:
              - entity: sensor.ham_radio_propagation_solar_hf_30_20_day
                name: Day
                show_state: true
                state_adaptive_color: true
              - entity: sensor.ham_radio_propagation_solar_hf_30_20_night
                name: Night
                show_state: true
                state_adaptive_color: true
            state_map:
              - value: Poor
                label: Poor
              - value: Fair
                label: Fair
              - value: Good
                label: Good
            smoothing: false
          - type: custom:mini-graph-card
            font_size: 75
            name: 17m - 15m
            hours_to_show: 48
            hour24: true
            upper_bound: 2
            lower_bound: 0
            min_bound_range: 2
            show:
              legend: false
            entities:
              - entity: sensor.ham_radio_propagation_solar_hf_17_15_day
                name: Day
                show_state: true
                state_adaptive_color: true
              - entity: sensor.ham_radio_propagation_solar_hf_17_15_night
                name: Night
                show_state: true
                state_adaptive_color: true
            state_map:
              - value: Poor
                label: Poor
              - value: Fair
                label: Fair
              - value: Good
                label: Good
            smoothing: false
          - type: custom:mini-graph-card
            font_size: 75
            name: 12m - 10m
            hours_to_show: 48
            hour24: true
            upper_bound: 2
            lower_bound: 0
            min_bound_range: 2
            show:
              legend: false
            entities:
              - entity: sensor.ham_radio_propagation_solar_hf_12_10_day
                name: Day
                show_state: true
                state_adaptive_color: true
              - entity: sensor.ham_radio_propagation_solar_hf_12_10_night
                name: Night
                show_state: true
                state_adaptive_color: true
            state_map:
              - value: Poor
                label: Poor
              - value: Fair
                label: Fair
              - value: Good
                label: Good
            smoothing: false
1 Like

Hi @arodland i’ve develop a module to get data from hamqsl.com and soon i want to connect to your json to retrieve MUF data, if you can add new API that automatically select nearest inonosonde to a given position (lat/lon) we can biuld a very good service.

thanks in advance.
emix
73

Can you add a sensor that detects Solar Flares?
I’m trying to find a way to create a sensor that detects Solar Flares. As an Ham Radio operator I will take advantage on that.
I’m not getting much info for HA, but there is a project that is close (https://community.home-assistant.io/…/solar-flux…/434299) but not relating the NOAA data into a sensor that results in a sensor that says “A Flare just happened! It’s a C9 flare”.
Can you help?

1 Like

Hi CT2HUU in the next week i’ll try to integrate this sensor.
thank you for your suggestion.

1 Like