Weather App with Voice Commands
Abstract
Section titled “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_recognitionwraps the mic and a cloud recognizer. - Why each failure mode (
UnknownValueError,RequestError) needs its own handling. - How to close the loop with text-to-speech for a hands-free experience.
Prerequisites
Section titled “Prerequisites”- Python 3.6 or above.
- A text editor or IDE, and a working microphone.
pip install SpeechRecognition requests pyaudio(PyAudio powers the mic; on Windowspip 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
Section titled “Getting Started”Create the project
Section titled “Create the project”- Create a folder named
voice-weather. - Inside it, create
weather_app_with_voice_commands.py. - Install dependencies and set your API key in the code.
Write the code
Section titled “Write the code”weather_app_with_voice_commands.py
pch.viewSource"""
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
Section titled “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.How it fits together
Section titled “How it fits together”Read from the top: this is what runs when you execute the file, and which function calls which. It is generated from the code, so it cannot drift from it.
flowchart TD RUN(["python weather_app_with_voice_commands.py"]) WeatherApp["WeatherApp
class"] main("main") RUN --> main main --> WeatherApp
Step-by-Step Explanation
Section titled “Step-by-Step Explanation”1. The recognizer and microphone
Section titled “1. The recognizer and microphone”recognizer = sr.Recognizer()
with sr.Microphone() as source:
audio = recognizer.listen(source)
city = recognizer.recognize_google(audio)Recognizer is the engine; Microphone() opens the default mic as a context manager (so it’s released cleanly). listen records until you stop talking, and recognize_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
Section titled “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.")Voice input is unreliable — background noise, mumbling, or no internet all fail differently. UnknownValueError = “I heard you but couldn’t parse it”; RequestError = “I couldn’t reach the API.” Distinct messages help the user fix the actual problem.
3. Fetching the weather
Section titled “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"]
...Same pattern as a normal weather app — the recognized city flows straight into the API query. Always check status_code before parsing.
4. Showing the result
Section titled “4. Showing the result”The formatted multi-line string lands in result_label. Next we’ll speak it instead.
Close the Loop: Text-to-Speech
Section titled “Close the Loop: Text-to-Speech”A voice app that only listens is half-built. Add spoken replies with pyttsx3 (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.")Now it’s hands-free: ask, and it answers aloud.
Don’t Freeze While Listening
Section titled “Don’t Freeze While Listening”recognizer.listen blocks — on the main thread the whole window freezes during recording. Run it on a background thread and update the UI via after:
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
Section titled “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)adjust_for_ambient_noise sets the energy threshold; timeout/phrase_time_limit stop it listening forever.
Common Mistakes
Section titled “Common Mistakes”| Problem | Cause | Fix |
|---|---|---|
PyAudio won’t install | Missing build tools / wheels | pipwin install pyaudio (Windows); brew install portaudio (Mac) |
| Always “couldn’t understand” | Noisy mic / bad threshold | adjust_for_ambient_noise first |
| Window freezes while listening | listen on the main thread | Listen on a background thread |
RequestError constantly | No internet (Google API needs it) | Check connection; use an offline engine like Vosk |
KeyError on weather data | Parsed before checking status | Guard with status_code == 200 |
| City misheard (“you York”) | Speech ambiguity | Show the recognized text; let the user confirm/retry |
Variations to Try
Section titled “Variations to Try”- Spoken replies — add
pyttsx3text-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"torecognize_google. - Voice everything — extend into a mini assistant (time, news, reminders).
- Visual feedback — animate a “listening” indicator while recording.
Real-World Applications
Section titled “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
Section titled “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
Section titled “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
Section titled “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.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading