Skip to content

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_recognition wraps 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 Windows pip install pipwin && pipwin install pyaudiopip install pipwin && pipwin install pyaudio if 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

  1. Create a folder named voice-weathervoice-weather.
  2. Inside it, create weather_app_with_voice_commands.pyweather_app_with_voice_commands.py.
  3. Install dependencies and set your API key in the code.

Write the code

weather_app_with_voice_commands.pySource
weather_app_with_voice_commands.py
"""
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.py
"""
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

command
C:\Users\Your Name\voice-weather> python weather_app_with_voice_commands.py
# Click "Speak", say a city like "London", and the weather appears.
command
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

weather_app_with_voice_commands.py
recognizer = sr.Recognizer()
with sr.Microphone() as source:
    audio = recognizer.listen(source)
    city = recognizer.recognize_google(audio)
weather_app_with_voice_commands.py
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

weather_app_with_voice_commands.py
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.")
weather_app_with_voice_commands.py
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

weather_app_with_voice_commands.py
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"]
    ...
weather_app_with_voice_commands.py
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):

speak.py
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.")
speak.py
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:

threaded.py
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))
threaded.py
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:

calibrate.py
with sr.Microphone() as source:
    recognizer.adjust_for_ambient_noise(source, duration=1)
    audio = recognizer.listen(source, timeout=5, phrase_time_limit=4)
calibrate.py
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

ProblemCauseFix
PyAudioPyAudio won’t installMissing build tools / wheelspipwin install pyaudiopipwin install pyaudio (Windows); brew install portaudiobrew install portaudio (Mac)
Always “couldn’t understand”Noisy mic / bad thresholdadjust_for_ambient_noiseadjust_for_ambient_noise first
Window freezes while listeninglistenlisten on the main threadListen on a background thread
RequestErrorRequestError constantlyNo internet (Google API needs it)Check connection; use an offline engine like Vosk
KeyErrorKeyError on weather dataParsed before checking statusGuard with status_code == 200status_code == 200
City misheard (“you York”)Speech ambiguityShow the recognized text; let the user confirm/retry

Variations to Try

  1. Spoken replies — add pyttsx3pyttsx3 text-to-speech (above).
  2. Wake word — listen continuously for “weather” before acting.
  3. Richer queries — parse “weather in Paris tomorrow” for a forecast.
  4. Offline recognition — swap Google for Vosk (no internet needed).
  5. Multi-language — pass language="es-ES"language="es-ES" to recognize_googlerecognize_google.
  6. Voice everything — extend into a mini assistant (time, news, reminders).
  7. 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 coffee

Was this page helpful?

Let us know how we did