Weather App with Voice Commands
Abstract
Talking to your computer feels like magic — and it’s surprisingly approachable. This project chains two services together: speech recognition (turn your voice into text) and a weather API (turn a city name into a forecast). You click a button, say a city, and the app speaks-or-shows the weather. You’ll learn how the microphone, recognizer, and Google’s speech API cooperate, then upgrade the experience with spoken replies (text-to-speech), a responsive threaded UI, and the error handling that voice apps absolutely require.
You will leave understanding:
- The capture → recognize → act pipeline behind every voice assistant.
- How
speech_recognitionspeech_recognitionwraps the mic and a cloud recognizer. - Why each failure mode (
UnknownValueErrorUnknownValueError,RequestErrorRequestError) needs its own handling. - How to close the loop with text-to-speech for a hands-free experience.
Prerequisites
- Python 3.6 or above.
- A text editor or IDE, and a working microphone.
pip install SpeechRecognition requests pyaudiopip install SpeechRecognition requests pyaudio(PyAudio powers the mic; on Windowspip install pipwin && pipwin install pyaudiopip install pipwin && pipwin install pyaudioif it fails).- A free OpenWeatherMap API key.
- Familiarity with the Simple Weather Forecast App helps — it covers the API half.
Getting Started
Create the project
- Create a folder named
voice-weathervoice-weather. - Inside it, create
weather_app_with_voice_commands.pyweather_app_with_voice_commands.py. - Install dependencies and set your API key in the code.
Write the code
weather_app_with_voice_commands.py
Source"""
Weather App with Voice Commands
A Python application that fetches weather information based on voice commands. Features include:
- Voice recognition to capture user queries.
- Fetching weather data from an API.
- Displaying weather information in a user-friendly format.
"""
import speech_recognition as sr
import requests
from tkinter import Tk, Label, Button, messagebox
API_KEY = "your_openweathermap_api_key"
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"
class WeatherApp:
def __init__(self, root):
self.root = root
self.root.title("Weather App with Voice Commands")
self.label = Label(root, text="Click the button and say a city name:")
self.label.pack(pady=10)
self.voice_button = Button(root, text="Speak", command=self.get_weather_by_voice)
self.voice_button.pack(pady=5)
self.result_label = Label(root, text="", wraplength=400, justify="left")
self.result_label.pack(pady=10)
def get_weather_by_voice(self):
"""Capture voice input and fetch weather information."""
recognizer = sr.Recognizer()
with sr.Microphone() as source:
try:
self.label.config(text="Listening...")
audio = recognizer.listen(source)
city = recognizer.recognize_google(audio)
self.label.config(text=f"You said: {city}")
self.fetch_weather(city)
except sr.UnknownValueError:
messagebox.showerror("Error", "Sorry, I could not understand the audio.")
except sr.RequestError:
messagebox.showerror("Error", "Could not request results, please check your internet connection.")
def fetch_weather(self, city):
"""Fetch weather data from the OpenWeatherMap API."""
params = {"q": city, "appid": API_KEY, "units": "metric"}
try:
response = requests.get(BASE_URL, params=params)
data = response.json()
if response.status_code == 200:
weather = data["weather"][0]["description"].capitalize()
temp = data["main"]["temp"]
feels_like = data["main"]["feels_like"]
humidity = data["main"]["humidity"]
result = (
f"Weather in {city}:\n"
f"Condition: {weather}\n"
f"Temperature: {temp}°C\n"
f"Feels Like: {feels_like}°C\n"
f"Humidity: {humidity}%"
)
self.result_label.config(text=result)
else:
messagebox.showerror("Error", data.get("message", "Failed to fetch weather data."))
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
def main():
root = Tk()
app = WeatherApp(root)
root.mainloop()
if __name__ == "__main__":
main()
"""
Weather App with Voice Commands
A Python application that fetches weather information based on voice commands. Features include:
- Voice recognition to capture user queries.
- Fetching weather data from an API.
- Displaying weather information in a user-friendly format.
"""
import speech_recognition as sr
import requests
from tkinter import Tk, Label, Button, messagebox
API_KEY = "your_openweathermap_api_key"
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"
class WeatherApp:
def __init__(self, root):
self.root = root
self.root.title("Weather App with Voice Commands")
self.label = Label(root, text="Click the button and say a city name:")
self.label.pack(pady=10)
self.voice_button = Button(root, text="Speak", command=self.get_weather_by_voice)
self.voice_button.pack(pady=5)
self.result_label = Label(root, text="", wraplength=400, justify="left")
self.result_label.pack(pady=10)
def get_weather_by_voice(self):
"""Capture voice input and fetch weather information."""
recognizer = sr.Recognizer()
with sr.Microphone() as source:
try:
self.label.config(text="Listening...")
audio = recognizer.listen(source)
city = recognizer.recognize_google(audio)
self.label.config(text=f"You said: {city}")
self.fetch_weather(city)
except sr.UnknownValueError:
messagebox.showerror("Error", "Sorry, I could not understand the audio.")
except sr.RequestError:
messagebox.showerror("Error", "Could not request results, please check your internet connection.")
def fetch_weather(self, city):
"""Fetch weather data from the OpenWeatherMap API."""
params = {"q": city, "appid": API_KEY, "units": "metric"}
try:
response = requests.get(BASE_URL, params=params)
data = response.json()
if response.status_code == 200:
weather = data["weather"][0]["description"].capitalize()
temp = data["main"]["temp"]
feels_like = data["main"]["feels_like"]
humidity = data["main"]["humidity"]
result = (
f"Weather in {city}:\n"
f"Condition: {weather}\n"
f"Temperature: {temp}°C\n"
f"Feels Like: {feels_like}°C\n"
f"Humidity: {humidity}%"
)
self.result_label.config(text=result)
else:
messagebox.showerror("Error", data.get("message", "Failed to fetch weather data."))
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
def main():
root = Tk()
app = WeatherApp(root)
root.mainloop()
if __name__ == "__main__":
main()
Run it
C:\Users\Your Name\voice-weather> python weather_app_with_voice_commands.py
# Click "Speak", say a city like "London", and the weather appears.C:\Users\Your Name\voice-weather> python weather_app_with_voice_commands.py
# Click "Speak", say a city like "London", and the weather appears.Step-by-Step Explanation
1. The recognizer and microphone
recognizer = sr.Recognizer()
with sr.Microphone() as source:
audio = recognizer.listen(source)
city = recognizer.recognize_google(audio)recognizer = sr.Recognizer()
with sr.Microphone() as source:
audio = recognizer.listen(source)
city = recognizer.recognize_google(audio)RecognizerRecognizer is the engine; Microphone()Microphone() opens the default mic as a context manager (so it’s released cleanly). listenlisten records until you stop talking, and recognize_googlerecognize_google sends the audio to Google’s free speech API and returns text. That’s the whole speech half in four lines.
2. Handling what can go wrong
except sr.UnknownValueError:
messagebox.showerror("Error", "Sorry, I could not understand the audio.")
except sr.RequestError:
messagebox.showerror("Error", "Could not request results, please check your internet connection.")except sr.UnknownValueError:
messagebox.showerror("Error", "Sorry, I could not understand the audio.")
except sr.RequestError:
messagebox.showerror("Error", "Could not request results, please check your internet connection.")Voice input is unreliable — background noise, mumbling, or no internet all fail differently. UnknownValueErrorUnknownValueError = “I heard you but couldn’t parse it”; RequestErrorRequestError = “I couldn’t reach the API.” Distinct messages help the user fix the actual problem.
3. Fetching the weather
params = {"q": city, "appid": API_KEY, "units": "metric"}
response = requests.get(BASE_URL, params=params)
data = response.json()
if response.status_code == 200:
weather = data["weather"][0]["description"].capitalize()
temp = data["main"]["temp"]
...params = {"q": city, "appid": API_KEY, "units": "metric"}
response = requests.get(BASE_URL, params=params)
data = response.json()
if response.status_code == 200:
weather = data["weather"][0]["description"].capitalize()
temp = data["main"]["temp"]
...Same pattern as a normal weather app — the recognized city flows straight into the API query. Always check status_codestatus_code before parsing.
4. Showing the result
The formatted multi-line string lands in result_labelresult_label. Next we’ll speak it instead.
Close the Loop: Text-to-Speech
A voice app that only listens is half-built. Add spoken replies with pyttsx3pyttsx3 (offline, no API):
import pyttsx3 # pip install pyttsx3
engine = pyttsx3.init()
def say(text):
engine.say(text)
engine.runAndWait()
# after fetching:
say(f"The weather in {city} is {weather}, {temp} degrees.")import pyttsx3 # pip install pyttsx3
engine = pyttsx3.init()
def say(text):
engine.say(text)
engine.runAndWait()
# after fetching:
say(f"The weather in {city} is {weather}, {temp} degrees.")Now it’s hands-free: ask, and it answers aloud.
Don’t Freeze While Listening
recognizer.listenrecognizer.listen blocks — on the main thread the whole window freezes during recording. Run it on a background thread and update the UI via afterafter:
import threading
def get_weather_by_voice(self):
threading.Thread(target=self._listen_and_fetch, daemon=True).start()
def _listen_and_fetch(self):
# ... listen + recognize + fetch ...
self.root.after(0, lambda: self.result_label.config(text=result))import threading
def get_weather_by_voice(self):
threading.Thread(target=self._listen_and_fetch, daemon=True).start()
def _listen_and_fetch(self):
# ... listen + recognize + fetch ...
self.root.after(0, lambda: self.result_label.config(text=result))Calibrate for Noise
In a noisy room, recognition accuracy plummets. Let the recognizer sample the ambient level first:
with sr.Microphone() as source:
recognizer.adjust_for_ambient_noise(source, duration=1)
audio = recognizer.listen(source, timeout=5, phrase_time_limit=4)with sr.Microphone() as source:
recognizer.adjust_for_ambient_noise(source, duration=1)
audio = recognizer.listen(source, timeout=5, phrase_time_limit=4)adjust_for_ambient_noiseadjust_for_ambient_noise sets the energy threshold; timeouttimeout/phrase_time_limitphrase_time_limit stop it listening forever.
Common Mistakes
| Problem | Cause | Fix |
|---|---|---|
PyAudioPyAudio won’t install | Missing build tools / wheels | pipwin install pyaudiopipwin install pyaudio (Windows); brew install portaudiobrew install portaudio (Mac) |
| Always “couldn’t understand” | Noisy mic / bad threshold | adjust_for_ambient_noiseadjust_for_ambient_noise first |
| Window freezes while listening | listenlisten on the main thread | Listen on a background thread |
RequestErrorRequestError constantly | No internet (Google API needs it) | Check connection; use an offline engine like Vosk |
KeyErrorKeyError on weather data | Parsed before checking status | Guard with status_code == 200status_code == 200 |
| City misheard (“you York”) | Speech ambiguity | Show the recognized text; let the user confirm/retry |
Variations to Try
- Spoken replies — add
pyttsx3pyttsx3text-to-speech (above). - Wake word — listen continuously for “weather” before acting.
- Richer queries — parse “weather in Paris tomorrow” for a forecast.
- Offline recognition — swap Google for Vosk (no internet needed).
- Multi-language — pass
language="es-ES"language="es-ES"torecognize_googlerecognize_google. - Voice everything — extend into a mini assistant (time, news, reminders).
- Visual feedback — animate a “listening” indicator while recording.
Real-World Applications
- Voice assistants — the Alexa/Siri/Google pattern in miniature.
- Accessibility — hands-free apps for users who can’t type.
- Smart home — voice control for devices and dashboards.
- In-car / kitchen apps — eyes-and-hands-busy contexts.
Educational Value
- Speech recognition — mics, recognizers, and cloud STT.
- Service chaining — wiring two APIs into one flow.
- Failure handling — voice and network are both unreliable.
- Accessible design — multimodal input/output.
Next Steps
- Add text-to-speech replies.
- Move listening to a background thread and calibrate for noise.
- Add a wake word and richer query parsing.
- Try offline recognition with Vosk.
Conclusion
You built a voice-controlled weather app by chaining speech recognition to a weather API, then made it conversational with text-to-speech and responsive with threading. The capture → recognize → act → respond loop is exactly how every voice assistant works — you’ve just built a focused one. Full source on GitHub. Explore more voice and API projects on Python Central Hub.
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
