Skip to content

Weather App with Voice Commands

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_recognition wraps 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.
  • Python 3.6 or above.
  • A text editor or IDE, and a working microphone.
  • pip install SpeechRecognition requests pyaudio (PyAudio powers the mic; on Windows pip 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.
  1. Create a folder named voice-weather.
  2. Inside it, create weather_app_with_voice_commands.py.
  3. Install dependencies and set your API key in the code.
weather_app_with_voice_commands.py pch.viewSource
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()
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.

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.

diagram Diagram mermaid
weather_app_with_voice_commands.py
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.

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. 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.

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_code before parsing.

The formatted multi-line string lands in result_label. Next we’ll speak it instead.

A voice app that only listens is half-built. Add spoken replies with pyttsx3 (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.")

Now it’s hands-free: ask, and it answers aloud.

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:

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))

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)

adjust_for_ambient_noise sets the energy threshold; timeout/phrase_time_limit stop it listening forever.

ProblemCauseFix
PyAudio won’t installMissing build tools / wheelspipwin install pyaudio (Windows); brew install portaudio (Mac)
Always “couldn’t understand”Noisy mic / bad thresholdadjust_for_ambient_noise first
Window freezes while listeninglisten on the main threadListen on a background thread
RequestError constantlyNo internet (Google API needs it)Check connection; use an offline engine like Vosk
KeyError on weather dataParsed before checking statusGuard with status_code == 200
City misheard (“you York”)Speech ambiguityShow the recognized text; let the user confirm/retry
  1. Spoken replies — add pyttsx3 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" to recognize_google.
  6. Voice everything — extend into a mini assistant (time, news, reminders).
  7. Visual feedback — animate a “listening” indicator while recording.
  • 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.
  • 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.
  • 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.

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.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading