The weather narrator demo comes in three parts:
weather_narrator.toml
This file contains the following configuration values that you can change.
-
voice- Which of the KittenTTS voices to use, there are 8 to choose from. See theavailable_voiceslist for valid options. -
location_points- This is a string containing comma separated National Weather Service X and Y grid points. See the NWS docs for more info about finding appropriate grid points. -
forecast_length- The number of periods that should be read aloud during the forecast returned by the NWS API. Typically this is one period for daytime, and another period for nighttime. Bigger numbers means read more future periods of the forecast. -
sound_device- The sound card identifier to play audio from i.e.plughw:3,0. This is only needed if sound doesn't play out of the voice bonnet by default on your device. See Voice Bonnet Setup for information about this value.
generate_forecast.py
This generate_forecast.py script fetches weather from the NWS API and uses KittenTTS to synthesize the current temperature and forecast into .wav files. It creates one file for each period up to the forcast_length specified in the config file. If you want to keep using this project and get updated weather information all the time, you could setup a cron job to run this script a few times a day based on your schedule.
code.py
This code.py script uses debouncer to poll the IO pin for the button on the voice bonnet. When the button is pressed, a .wav sound file is generated with the current date and time, then it and all of the previously generated weather forecast files are played one after another.
Run The Project
To use this project click the Download Project Bundle button below, unzip the file, and copy the downloaded files to your Raspberry Pi. The CircuitPython versions and lib/ folder inside the download can be ignored. Only code.py, generate_forecast.py, and weather_narrator.toml are needed.
Activate the virtual environment with Blinka and KittenTTS installed in it. Install the Adafruit Debouncer library with this command:
pip install adafruit-circuitpython-debouncer
Now run the downloaded python scripts with these commands:
python generate_forecast.py python code.py
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import json
import time
import glob
import os
import tomllib
from datetime import datetime
import board
from digitalio import DigitalInOut, Direction, Pull
from adafruit_debouncer import Debouncer
from kittentts import KittenTTS
import soundfile as sf
print("initializing...")
with open("weather_narrator.toml", "rb") as f:
config = tomllib.load(f)
voice = config.get("voice", None)
sound_device = config.get("sound_device", None)
day_of_month_words = [
"1st",
"2nd",
"3rd",
"4th",
"5th",
"6th",
"7th",
"8th",
"9th",
"10th",
"11th",
"12th",
"13th",
"14th",
"15th",
"16th",
"17th",
"18th",
"19th",
"20th",
"21st",
"22nd",
"23rd",
"24th",
"25th",
"26th",
"27th",
"28th",
"29th",
"30th",
"31st",
]
button = DigitalInOut(board.D17)
button.direction = Direction.INPUT
button.pull = Pull.UP
debounced_btn = Debouncer(button)
with open("forecast.json", "r") as f:
forecast = json.load(f)
m = KittenTTS("KittenML/kitten-tts-nano-0.2")
def generate_date_time_audio(date_obj):
replacements = {
"00": "oh clock",
"01": "oh 1",
"02": "oh 2",
"03": "oh 3",
"04": "oh 4",
"05": "oh 5",
"06": "oh 6",
"07": "oh 7",
"08": "oh 8",
"09": "oh 9",
}
now_date_obj = datetime.now()
try:
os.remove("date.wav")
except FileNotFoundError:
pass
month = date_obj.strftime("%B")
day_word = day_of_month_words[date_obj.day - 1]
date_script = f"{month} {day_word}, {date_obj.year}."
time_script = now_date_obj.strftime("%-I %M %p")
for key, val in replacements.items():
time_script = time_script.replace(key, val)
date_script += f" The time is: {time_script}."
audio = m.generate(date_script, voice=voice)
sf.write("date.wav", audio, 24000)
print("Press button to hear time and weather...")
while True:
debounced_btn.update()
if debounced_btn.fell:
print("just pressed")
dt_format = "%Y-%m-%dT%H:%M:%S%z"
forecast_date_obj = datetime.strptime(
forecast["properties"]["periods"][0]["startTime"], dt_format
)
generate_date_time_audio(forecast_date_obj)
files_to_read = glob.glob("sound_files/*.wav")
sorted_files_asc = sorted(files_to_read, key=os.path.getmtime)
sorted_files_asc.insert(0, "date.wav")
for file in sorted_files_asc:
if sound_device is None:
os.system(f"aplay {file}")
else:
os.system(f"aplay -D {sound_device} {file}")
time.sleep(0.01)
The script will take a moment to initialize and then print a message indicating it's ready for the button to be pressed.
$ python code.py initializing... Press button to hear time and weather...
Press the button, and wait a few seconds while it generates the date/time file, then it plays all audio files out of the speakers.
Page last edited August 12, 2025
Text editor powered by tinymce.