On Demand News Headlines Using Voice Preview & TTS

After getting a “daily briefing” script to work with Voice Preview the next thing was to get the 5 latest BBC news headlines from the RSS feed and play then on-demand. Should be easy, right? Famous last words!

The environment is HAOS with Home Assistant Voice Preview Edition and a Gemini plug in. There were a number of issues to overcome, one of these being understanding the Docker environment. But it’s now working and has been a great learning experience. (Cavet: It’s working at the time of writing, but the BBC may make changes.)

Overview

The end goal was to use TTS to play the top 5 news stories on the Voice Preview as a satellite device without using the AI integration.

There are limitations within Home Assistant not being a full OS and, in this case, running as Docker. This is not a complaint, just an observation that needed to be considered as the steps below could probably be simplified by doing all of this from a full Linux OS. Also, I didn’t want to rely on a whole heap of add-ons. The best way to work around these issues was to use a bash and a python script for the heavy lifting.

Broadly, the steps are,

  • Download the RSS page as a local XML file
  • Parse the XML file to generate a local JSON file containing the first 5 news stories Title and Description values and stripping out CDATA wrapper
  • Put the JSON file into a Home Assistant sensor
  • Use the sensor data for the TTS announcements

Base Script Setup

First thing is to setup the folder and create the scripts. (Don’t blame me for the code. It was all AI driven using a mix of Co-pilot, Gemini & Grok. Including repeatedly using one or more AI’s to fix output from the other AI.)

SSH

mkdir /config/shell

Retrieval Script - get_bbc_news.sh

#!/bin/sh
curl -s "https://feeds.bbci.co.uk/news/rss.xml" > /config/shell/bbc_news.xml

Processing Script - parse_bbc_news.py

import xml.etree.ElementTree as ET
import json

# Define paths
xml_file = "/config/shell/bbc_news.xml"
json_file = "/config/shell/bbc_news.json"

def parse_bbc_news(xml_file):
    """Parse BBC News RSS XML and extract first 5 entries for titles and descriptions."""
    try:
        tree = ET.parse(xml_file)
        root = tree.getroot()

        # Navigate to the <channel> -> <item> section
        items = root.find("channel").findall("item")

        # Extract first 5 items
        news_data = []
        for item in items[:5]:
            title = item.find("title").text.strip()
            description = item.find("description").text.strip()

            # Remove CDATA if present
            if title.startswith("<![CDATA[") and title.endswith("]]>"):
                title = title[9:-3]
            if description.startswith("<![CDATA[") and description.endswith("]]>"):
                description = description[9:-3]

            news_data.append({"title": title, "description": description})

        return {"articles": news_data}

    except Exception as e:
        return {"error": f"Failed to parse XML: {str(e)}"}

def write_to_json(data, json_file):
    """Write extracted data to a JSON file."""
    try:
        with open(json_file, "w") as file:
            json.dump(data, file, indent=4)

        # Replace print with logging
        logger.info(f"Successfully written to {json_file}")
    except Exception as e:
        logger.error(f"Error writing to JSON: {str(e)}")

SSH – Make them executable

chmod +x /config/shell/get_bbc_news.sh

chmod +x /config/shell/parse_bbc_news.py

At this point you can run the get_bbc_news.sh script from ssh which will create the /config/shell/ bbc_news.xml file. But read on…

Base HA Setup

Now that we have the scripts, it’s time to allow HA access to the /config/shell folder, setup an automation to run them every 20 minutes and create the sensor that will use /config/shell/ bbc_news.json.

HA – configuration.yaml

homeassistant:
  allowlist_external_dirs:
    - "/config/shell"

python_script:

shell_command:
  get_bbc_news: "bash /config/shell/get_bbc_news.sh"
  parse_bbc_news: "python3 /config/shell/parse_bbc_news.py"

# configuration.yaml entry for BBC News Command Line Sensor
command_line:
  - sensor:
      name: "BBC News Headlines"
      unique_id: bbc_news_headlines_sensor
      command: >
        cat /config/shell/bbc_news.json | tr -d '\047' | jq '.articles | map({"title":.title, "description":.descrip>
      scan_interval: 300 # 5 minutes
      value_template: "{{ value_json.titles | length if value_json.titles is defined else 0 }}"
      json_attributes:
        - titles
        - descriptions
      icon: mdi:newspaper

The YAML code for the automation is below. it’s good to set this up in the UI as doing so makes it easy to run the automation on demand and use the Trace feature for troubleshooting. Within the Trace feature is a Changed Variable tab, so you can look at the steps and see what is changing.

AUTOMATION SCRIPT - Trigger Get BBC News

alias: Trigger Get BBC News
description: ""
triggers:
  - trigger: time_pattern
    minutes: /20
conditions: []
actions:
  - action: shell_command.get_bbc_news
    metadata: {}
    data: {}
  - action: shell_command.parse_bbc_news
    metadata: {}
    data: {}
mode: single

In the developer tools, check the config and restart Home Assistant. Don’t try and save time with restarts by doing reloads, some of the sensor changes need a full restart. It’s the first troubleshooting step if things don’t work, followed by checking the logs.

So, if this is working there should be,

  • A file /config/shell/ bbc_news.xml
  • A file /config/shell/ bbc_news.json
  • A sensor in the Developer tools > States called sensor.bbc_news_headlines with the news entries in it.

Say Something

Now we have a sensor with the headline title and description it’s time to say something. This is using the Voice Preview to announce but could be adapted for other media.

SCRIPT - VA - Read BBC News Headlines

alias: VA - Read BBC News Headlines
sequence:
  - sequence:
      - data:
          message: >
            {% set announcements = ["Here is the news", "The headlines are",
            "The latest news", "This is the news"      ] %} {{ announcements |
            random }}
          preannounce: false
        target:
          entity_id: assist_satellite.home_assistant_voice_[YOUR ID]_assist_satellite
        action: assist_satellite.announce
  - repeat:
      count: "{{ states.sensor.bbc_news_headlines.attributes.titles | length }}"
      sequence:
        - data:
            message: >
              {{ states.sensor.bbc_news_headlines.attributes.titles[repeat.index
              - 1] }}
            preannounce: true
          action: assist_satellite.announce
          target:
            entity_id: assist_satellite.home_assistant_voice_[YOR ID]_assist_satellite
        - delay:
            seconds: 0.5
        - data:
            message: >
              {{
              states.sensor.bbc_news_headlines.attributes.descriptions[repeat.index
              - 1] }}
            preannounce: false
          action: assist_satellite.announce
          target:
            entity_id: assist_satellite.home_assistant_voice_[YOUR ID_assist_satellite
        - delay:
            seconds: 2
description: ""

That’s the hard part done. Next step is to trigger the script using voice. While I could probably incorporate the script into this keeping it sperate and calling it means it can be triggered other ways as well as by voice activation.

However, there seem to be two gotcha’s with using Voice Preview.

  1. The automation uses trigger phrases. If any of the trigger phrases has an apostrophe, such as “what’s”, the script does not run. This needs to be changes to “whats”.
  2. The setup includes the Gemini AI, but this integration is local and so should not be interacting with the AI. It seems though that Voice Preview has some built in generic phrases that trigger the AI. If I used a generic trigger phrase, “whats going on”, then it would trigger the script, but the script would stop after about a second and the AI integration would take over. It was just trial and error to identify these phrases.

To use this active the Voice Preview with the wake word (“Hey Mycroft” etc) followed by a trigger phase in the script. This automation simply waits for the trigger phrase then calls the script above.

AUTOMATION VA - SPEAK

alias: VA - News Headlines
description: Read the news headlines from the BBC
triggers:
  - trigger: conversation
    command:
      - Read the news
      - Give me the headlines
      - Whats the latest news
      - What are the headlines
      - Tell me the news
      - Whats the news
conditions: []
actions:
  - set_conversation_response: "\"\"      "
    enabled: true
  - action: script.speak_bbc_news_headlines_2
    metadata: {}
    data: {}
mode: single

Fin

That’s all folks. Hopefully this helps someone.

1 Like