neon.ninja

Sharing enlightening moments of home automation

Fire Danger Rating

Each time I drive past our local fire station I see that fire danger rating sign and take a quick look. Wouldn’t it be great to have this information right in your favourite home automation system?

Luckily the NSW Rural Fire Service provides an XML feed that contains the fire danger details for today and tomorrow for districts in the state.

There is currently no simple way that I am aware of to scrap random XML in Home Assistant and convert and then display values from that XML as sensor values.

I decided to develop a small custom platform that retrieves the XML and then stores the relevant values in state attributes. You can then use template sensors to extract the data you actually want to display.

Just a quick note on using custom components in Home Assistant: The code may or may not work in future versions of Home Assistant.

Installation

Source code, documentation and examples can be found in my Github repository exxamalte/home-assistant-customisations.

Install custom component code

There has recently been an announcement that custom component code will need to follow a different folder structure in the future. We are currently seeing a transition between the two structures, and in the current 0.88 release both ways should still work, but I’d recommend migrate to the new structure now if you’re on that release and not wait until the old structure is not accepted anymore.

Also, another change in version 0.91 required a code fix in this custom component.

Install code on versions >=0.91

In your configuration folder create subfolder <config>/custom_components and copy the folder <a href="https://github.com/exxamalte/home-assistant-customisations/tree/master/nsw-rural-fire-service-fire-danger/config/custom_components/nsw_rural_fire_service_fire_danger">nsw_rural_fire_service_fire_danger</a> into that new
custom_components folder.

The latest version of this custom component on GitHub works without any modifications.

Install code on versions 0.90, 0.89 and 0.88

In your configuration folder create subfolder <config>/custom_components and copy the folder <a href="https://github.com/exxamalte/home-assistant-customisations/tree/master/nsw-rural-fire-service-fire-danger/config/custom_components/nsw_rural_fire_service_fire_danger">nsw_rural_fire_service_fire_danger</a> into that new
custom_components folder.

Due to a change in the Home Assistant code structure from version 0.91 onwards, you will need to change one line in this custom component code:

Line 14 needs to be changed from

from homeassistant.components.rest.sensor import RestData

to

from homeassistant.components.sensor.rest import RestData

Install code on versions <0.88

In your configuration folder create subfolders <config>/custom_components/sensor and copy the file <a href="https://github.com/exxamalte/home-assistant-customisations/blob/master/nsw-rural-fire-service-fire-danger/config/custom_components/nsw_rural_fire_service_fire_danger/sensor.py">sensor.py</a> into that new folder and rename it to nsw_rural_fire_service_fire_danger.py.

The small code change described in the previous section must be applied for for installations on versions <0.88, too.

Install dependencies

This custom component uses the third-party library xmltodict which you may need to install manually from the command line, into your virtual environment if you are running Home Assistant in venv:

pip install xmltodict

Initial setup of the retrieval sensor

Have a look at the XML feed at http://www.rfs.nsw.gov.au/feeds/fdrToban.xml and find your district. The district’s name must be configured as district_name as shown in the following example:

sensor:  
  - platform: nsw_rural_fire_service_fire_danger
    district_name: Greater Sydney Region

After a restart of Home Assistant, a new sensor should now show up. In this example its entity id will be sensor.fire_danger_in_greater_sydney_region, so your entity id will be different if you have chosen a different district.

Fire danger retrieval sensor

The retrieval sensor’s state will either be ok or unknown, depending on if it was able to retrieve data from the XML feed or not, and you will find all the interesting bits in its state attributes.

AttributeDescription
districtDistrict name
region_numberInternal number of this district
councilsList of all councils in this district
danger_level_todayToday’s danger level
danger_level_tomorrowTomorrow’s danger level
fire_ban_todayIndicates whether there is a fire ban today
fire_ban_tomorrowIndicates whether there is a fire ban today

By default, the retrieval sensor updates from the feed every 10 minutes which should be fine under normal circumstances. You can change this by modifying the line SCAN_INTERVAL = timedelta(minutes=10) in the sensor’s code and pick a different interval.

Fire danger retrieval sensor more info dialogue

Configuring template sensors

At a minimum you are probably interested in today’s conditions, so let’s add two new template sensors, one that shows today’s danger level and the other one indicating whether there is a fire ban today.

In the below examples I am using my previously configured sensor from above, so please check your sensor’s entity id and replace that in the configuration below.

Fire Ban Today

The fire ban indicator is already transformed into a true/false value above, and can be used in a template binary sensor as follows:

binary_sensor:
  - platform: template
    sensors:
      fire_ban_today:
        friendly_name: "Fire Ban Today"
        value_template: "{{ state_attr('sensor.fire_danger_in_greater_sydney_region', 'fire_ban_today') }}"
        device_class: safety

I am using the safety device class here which I believe works well in badge mode:

Fire ban today off as badge
Fire ban today on as badge

But it may be a bit misleading when presented in a group. If there is a fire ban, the XML feed will contain the value “yes” which is turned into “true” in the retrieval sensor above. The safety device class defines “true” as unsafe and displays a warning sign. Likewise the badge displays as a shield with tick if the sensor value is “false”. However, the textual representation would be “Safe” = no fire ban, or “Unsafe” = fire ban. Please have a think about if this works for you or not.

Fire ban today on

Danger Level Today

The danger level is a textual description that can be used in a template sensor as follows:

sensor:
  - platform: template
    sensors:
      fire_danger_level_today:
        friendly_name: "Danger Level Today"
        value_template: "{{ state_attr('sensor.fire_danger_in_greater_sydney_region', 'danger_level_today') }}"
        icon_template: mdi:speedometer
Fire danger level today

Hiding the retrieval sensor

Once all sensors are fully set up you will probably want to hide the retrieval sensor:

homeassistant:
  customize:
    sensor.fire_danger_in_greater_sydney_region:
      hidden: true

Notification

A simple notification would just let you know if the Danger Level changes. From experience, if the actual Danger Level changes then the XML feed is updated at midnight, and so will be your sensor.

Here is a sample automation sending an HTML formatted pushover notification. The condition is just there to avoid the cases where the XML feed is temporarily unavailable.

- id: fire_danger_level_today_changed
  alias: "Danger Level Changed"
  trigger:
    - platform: state
      entity_id: sensor.fire_danger_level_today
  condition:
    condition: template
    value_template: "{{ (trigger.from_state.state != 'None') and (trigger.to_state.state != 'None') }}"
  action:
    - service: notify.pushover
      data_template:
        message: "Danger level changed to {{ trigger.to_state.state }} (was {{ trigger.from_state.state }}).
Fire Ban Today: {{ states.binary_sensor.fire_ban_today.state }}"
        title: "Fire Danger Level Changed"
        data:
          html: 1

Outlook

So far I find it useful to have the fire danger information available in Home Assistant, but would yet need to come up with any more sophisticated use-cases – other than a simple notification – to make use of this information.

At this point in time I am not sure if it is worth it transforming this custom component into a proper contribution for Home Assistant. What do you think?

The XML feed provided by the NSW Rural Fire Service is in a custom format, and I haven’t found any comparable feeds from other states. If anyone is aware of anything suitable, please let me know.

Compatibility

At the time of writing this post, I used:

  • Home Assistant 0.86.3 with Python 3.6.7

Update 04 Feb 2019

After publishing this on the Home Assistant community forum, I received some good feedback on this topic, and I just wanted to share one improvement in particular:

User DavidFW1960 is translating the fire danger rating into a dial gauge icon that looks just like the ones you actually see at your local fire station, and shares his Lovelace configuration and icons.

Update 22 Feb 2019

With the recent announcement to overhaul the structure of how custom components have to be stored, I wanted to be a bit proactive, and have modified the above post to reflect the new structure.

Update 31 Mar 2019

Home Assistant version 0.91 will contain a small restructure which in turn requires a small fix of this custom component. The latest version on GitHub already contains this fix.

Update 31 Oct 2020

Home Assistant 0.117 contains some structural changes that require a fix which is available on my GitHub repository now.

Also, I have worked on a separate repository which is compatible with HACS to the make the installation a bit easier. Watch out for more details on this blog very soon.

Comments

2 responses to “Fire Danger Rating”

  1. EBF Avatar
    EBF

    Hi, I posted this reply on your thread in HA, but in case you don’t see it, thought I should reply here too.

    I’m having troubles getting this started – I have a sneaking suspicion it has something to do with the third-party library and my installing it incorrectly. I’m running the latest version of Hassbian. Here’s what I do.

    Upon logging in via Putty:

    sudo -u homeassistant -H -s
    pip install xmltodict
    Collecting xmltodict
    Downloading https://files.pythonhosted.org/packages/28/fd/30d5c1d3ac29ce229f6bdc40bbc20b28f716e8b363140c26eff19122d8a5/xmltodict-0.12.0-py2.py3-none-any.whl
    Installing collected packages: xmltodict
    Successfully installed xmltodict-0.12.0

    I then proceed to follow the rest of your instructions – creating a folder in custom_components called `nsw_rural_fire_service_fire_danger` and then placing `sensory.py` and `__init__.py` into that folder.

    Then, in configuration.yaml, place the following lines:

    sensor:
    #RFS
    – platform: nsw_rural_fire_service_fire_danger
    district_name: Greater Sydney Region

    This is the resulting error in the logs:

    Error handling request
    Traceback (most recent call last):
    File “/srv/homeassistant/lib/python3.5/site-packages/aiohttp/web_protocol.py”, line 418, in start
    resp = await task
    File “/usr/lib/python3.5/asyncio/futures.py”, line 380, in __iter__
    yield self # This tells Task to wait for completion.
    File “/usr/lib/python3.5/asyncio/tasks.py”, line 304, in _wakeup
    future.result()
    File “/usr/lib/python3.5/asyncio/futures.py”, line 293, in result
    raise self._exception
    File “/usr/lib/python3.5/asyncio/tasks.py”, line 241, in _step
    result = coro.throw(exc)
    File “/srv/homeassistant/lib/python3.5/site-packages/aiohttp/web_app.py”, line 458, in _handle
    resp = await handler(request)
    File “/srv/homeassistant/lib/python3.5/site-packages/aiohttp/web_middlewares.py”, line 119, in impl
    return await handler(request)
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/http/real_ip.py”, line 33, in real_ip_middleware
    return await handler(request)
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/http/ban.py”, line 68, in ban_middleware
    return await handler(request)
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/http/auth.py”, line 98, in auth_middleware
    return await handler(request)
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/http/view.py”, line 112, in handle
    result = await result
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/config/core.py”, line 21, in post
    errors = await async_check_ha_config_file(request.app[‘hass’])
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/config.py”, line 779, in async_check_ha_config_file
    check_ha_config_file, hass)
    File “/usr/lib/python3.5/asyncio/futures.py”, line 380, in __iter__
    yield self # This tells Task to wait for completion.
    File “/usr/lib/python3.5/asyncio/tasks.py”, line 304, in _wakeup
    future.result()
    File “/usr/lib/python3.5/asyncio/futures.py”, line 293, in result
    raise self._exception
    File “/usr/lib/python3.5/concurrent/futures/thread.py”, line 55, in run
    result = self.fn(*self.args, **self.kwargs)
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/scripts/check_config.py”, line 371, in check_ha_config_file
    platform = loader.get_platform(hass, domain, p_name)
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/loader.py”, line 86, in get_platform
    component = _load_file(hass, platform_name, LOOKUP_PATHS)
    File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/loader.py”, line 166, in _load_file
    module = importlib.import_module(path)
    File “/usr/lib/python3.5/importlib/__init__.py”, line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
    File “”, line 986, in _gcd_import
    File “”, line 969, in _find_and_load
    File “”, line 958, in _find_and_load_unlocked
    File “”, line 673, in _load_unlocked
    File “”, line 669, in exec_module
    File “”, line 775, in get_code
    File “”, line 735, in source_to_code
    File “”, line 222, in _call_with_frames_removed
    File “/home/homeassistant/.homeassistant/custom_components/nsw_rural_fire_service_fire_danger/__init__.py”, line 7

    ^
    SyntaxError: invalid syntax

    1. malte Avatar
      malte

      As mentioned in the HA community forum:

      When you manually copy files from GitHub, please make sure that you copy the raw file, not the HTML source of the file representation (i.e. don’t right-click on a filename and then “Save file as”). There is a “Raw” button in the UI that gives you the actual Python source code.

Leave a Reply to EBF Cancel reply

Your email address will not be published. Required fields are marked *