Weather & Wardrobe Assistant
This project is an assistant that will read the weather and make a suggestion for wardrobe based on the forecast conditions. Thanks to multi-lingual support in both SmolLM3 and Piper TTS, the assistant can translate the weather and wardrobe message from English into 5 other languages: French, German, Portuguese, Italian, and Spanish.
Generating the wardrobe suggestion, translating the text, and synthesizing it to speech all occurs locally on the Raspberry Pi without relying on any cloud services. During testing, the full process took an average of 2-3 minutes. Perhaps not fast enough to be done on demand at the user's request, but pretty quick considering the tasks involved and Pi 5 hardware.
make_weather_wardrobe_audio.py
This script fetches the current conditions and forecast from weather.gov for a given set of location points. After fetching the data, the script first sends it to a SmolLM3 instance that is tasked with suggesting appropriate wardrobe items based on the weather conditions. Next it combines the wardrobe suggestion with the forecast text and sends them to another SmolLM3 instance, this time to translate the passage of text into the chosen language. Once the translation is complete, the resulting text is passed to Piper TTS to synthesize it as speech into a wave audio 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 once or a few times a day based on your schedule.
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import argparse
import json
from pathlib import Path
import os
import shutil
import wave
import requests
from ollama import chat
from ollama import ChatResponse
from piper import PiperVoice
# pylint: disable=line-too-long
parser = argparse.ArgumentParser(
prog="python generate_translated_weather_audio.py",
description="Multi-Lingual Weather & Wardrobe Assistant - "
"Fetches weather conditions from weather.gov for a given set of location points. "
"Generates a wardrobe suggestion based on the weather conditions. "
"Translates the weather and wardrobe suggestion into one of 5 other languages. "
"Synthesizes a wave audio file narrating the weather and wardrobe info in "
"the specified language.",
epilog="Made with: SmolLM3 & Piper1-gpl",
)
parser.add_argument(
"-l",
"--language",
default="es",
help="The language to translate into. One of (de, es, fr, it, pt). Default is es.",
)
parser.add_argument(
"-p",
"--location-points",
default="36,33",
help="The weather.gov API location points to get weather for. Default is 36,33. "
"Visit https://api.weather.gov/points/{lat},{lon} to find location points "
"for GPS coordinates",
)
parser.add_argument(
"-e",
"--period",
default="current",
help="The weather period to consider, current or next. Default is current.",
)
parser.add_argument(
"-c",
"--cached",
action="store_true",
help="Use the cached weather data from forecast.json instead of fetching from the server.",
)
args = parser.parse_args()
language_name_map = {
"es": "spanish",
"de": "german",
"fr": "french",
"it": "italian",
"pt": "portuguese",
}
language_voice_map = {
"es": "es_MX-claude-high.onnx",
"de": "de_DE-kerstin-low.onnx",
"fr": "fr_FR-upmc-medium.onnx",
"it": "it_IT-paola-medium.onnx",
"pt": "pt_BR-jeff-medium.onnx",
}
if args.language not in language_name_map.keys(): # pylint: disable=consider-iterating-dictionary
raise ValueError(
f"Invalid language {args.language}. Valid languages are {language_name_map.keys()}"
)
if args.period.lower() not in {"current", "cur", "next"}:
raise ValueError(
f"Invalid period {args.period}. Valid periods are 'current', 'next'"
)
replacements = {"mph": "miles per hour"}
# latlng_lookup_url = "https://api.weather.gov/points/{lat},{lon}"
location_points = args.location_points
if not args.cached:
weather_data = requests.get(
f"https://api.weather.gov/gridpoints/TOP/{location_points}/forecast", timeout=20
).json()
print("Fetched weather...")
with open("forecast.json", "w") as f:
json.dump(weather_data, f)
else:
weather_data = json.loads(Path("forecast.json").read_text())
print("Read cached weather...")
period_index = 0
if args.period == "next":
period_index = 1
elif args.period in {"cur", "current"}:
period_index = 0
period = weather_data["properties"]["periods"][period_index]
english_weather = (
f'Current Temperature is {period["temperature"]}{period["temperatureUnit"]}. '
)
english_weather += f'{period["name"]} {period["detailedForecast"]}'
for key, replacement in replacements.items():
english_weather = english_weather.replace(key, replacement)
print(f"english_weather: {english_weather}")
print("Generating wardrobe suggestion...")
response: ChatResponse = chat(
model="translator-smollm3",
messages=[
{
"role": "system",
"content": "You are a wardrobe assistant. Your job is to suggest some appropriate "
"clothes attire for a person to wear based on the weather. You can include clothing items "
"and accessories that are appropriate for the specified weather conditions. "
"Use positive and re-affirming language. Do not output any explanations, "
"only output the wardrobe suggestion. Do not summarize the weather."
"The wardrobe suggestion output should be no more than 2 sentences.",
},
{
"role": "user",
"content": f"{english_weather}",
},
],
)
print(response["message"]["content"])
# combine weather and wardrobe suggestion
english_weather += " " + response["message"]["content"]
print("Translating weather & wardrobe...")
language = language_name_map[args.language]
response: ChatResponse = chat(
model="translator-smollm3",
messages=[
{
"role": "system",
"content": "You are a translation assistant. The user is going to give you a short passage in english, "
f"please translate it to {language}. Output only the {language} translation of the input. "
"Do not output explanations, notes, or anything else. If there is not an exact literal translation, "
"just output the best fitting alternate word or phrase that you can. Do not explain anything, "
f"only output the translation. All output should be in {language}",
},
{
"role": "user",
"content": f"{english_weather}",
},
],
)
translated_weather = response["message"]["content"]
print(translated_weather)
print("Generating audio...")
shutil.rmtree("sound_files", ignore_errors=True)
os.mkdir("sound_files")
voice = PiperVoice.load(language_voice_map[args.language])
with wave.open("sound_files/weather_and_wardrobe.wav", "wb") as wav_file:
voice.synthesize_wav(translated_weather, wav_file)
print("Audio generation complete...")
To run it, be sure to activate the project virtual environment and then use this command.
python make_weather_wardrobe_audio.py
By default it will fetch weather for New York and translates into Spanish. There are a number of arguments that can be used to change its behavior:
-
-lor--language: Specify a language to translate to, orento stay in English . The valid options arees,de,fr,it,pt, anden -
-por--location-points: A pair of X,Y NWS location points. Must be separated by a comma, without spaces. For example36,33. See the NWS docs for more info about finding appropriate grid points. -
-eor--period: Which weather period to use. Valid values arecurrentornext. -
-cor--cached: Use the cached copy of forecast data instead of fetching from NWS. Use this if you're translating weather from the same location multiple times into different languages. It prevents the script from continuing to fetch data from the NWS API several times when it won't have changed.
Play Audio
Once the translation is complete and synthesized into the wave file, you can play it using the following command.
aplay sound_files/weather_and_wardrobe.wav
Page last edited November 12, 2025
Text editor powered by tinymce.