Skip to main content

PC Voice Assistant

A local, privacy-focused voice assistant for Windows that runs entirely on your machine. Speak naturally to control your PC — open apps, set reminders, search the web, and more.

Features

  • Wake Word Detection: Say "Alexa" or "Hey Computer" to activate (ML-based openWakeWord with energy fallback)
  • Speech Recognition: Offline Vosk ASR engine — no internet required
  • Natural Language Understanding: Rule-based intent matching with optional Ollama LLM integration
  • Text-to-Speech: pyttsx3 (offline) or edge-tts (online, higher quality)
  • Smart Reminders: Natural time expressions ("in 2 hours", "next Monday at 3pm")
  • Fuzzy App Matching: "open chrome" matches "Google Chrome" automatically
  • System Integration: Open apps, control volume, reminders, screenshots, and more
  • System Tray: Runs in background with tray icon and context menu
  • Rotating Logs: All activity logged to logs/assistant.log
  • 133 Test Suite: Comprehensive unit tests for all modules

1. Project Overview

The PC Voice Assistant is a Python CLI application that provides hands-free voice control for Windows PCs. It listens for a wake word, records your voice, transcribes it to text, understands your intent, executes the action, and speaks a response.

Pipeline Flow:

Wake Word → Record Audio (VAD) → Transcribe → Parse Intent → Execute Action → Speak Response → Wait for Wake Word

The assistant is:

  • Local/Offline: Core functionality works without internet
  • Windows-Focused: Uses Windows-specific commands (PowerShell, Windows APIs)
  • Privacy-First: All processing happens on your machine
  • Extensible: Easy to add new intents and commands

2. Requirements

Python Version

  • Python 3.10 or higher required (union type hints like Tuple[...] | None)

Operating System

  • Windows (primary) — fully supported
  • Linux/Mac: Wake word and TTS modules may work, but system commands (volume, shutdown, sleep) require Windows

Hardware

  • Microphone required for voice input
  • Any standard USB or built-in microphone works
  • Audio output device required for TTS

External Tools (Optional)

Tool Purpose Install
Ollama LLM-powered responses ollama pull llama3.2
Vosk Model Offline speech recognition Download from alphacephei.com
edge-tts Online TTS (better quality) pip install edge-tts
plyer Desktop notifications pip install plyer

3. Installation

Step 1: Clone the Repository

git clone <repository-url>
cd pc-assistant

Step 2: Install Python Dependencies

pip install -r requirements.txt

Step 3: Download and Install Vosk Model

  1. Download from: https://alphacephei.com/vosk/models
  2. Recommended model: vosk-model-small-en-us-0.15 (~45MB) or vosk-model-en-us-0.22 (~1.8GB)
  3. Extract into the project root directory
  4. The folder name should match asr.model_path in config.json (default: models/vosk-model-small-en-us-0.15)
pc-assistant/
├── models/
│   └── vosk-model-small-en-us-0.15/
│       ├── am/
│       ├── conf/
│       ├── graph/
│       └── ivector/
├── main.py
├── config.json
└── ...

Step 4: (Optional) Install and Start Ollama

For LLM-powered natural language understanding:

  1. Install Ollama: https://ollama.ai
  2. Pull a model:
    ollama pull llama3.2
    
  3. Start Ollama server:
    ollama serve
    
  4. Set nlu.use_ollama to true in config.json

Step 5: Run the Assistant

python main.py

Press Ctrl+C to exit gracefully.


4. Configuration (config.json)

All settings are in config.json. Never edit while the assistant is running.

wake_word Section

Key Type Default Description
enabled bool true Enable wake word detection
engine string "openwakeword" Detection engine: "openwakeword" or "energy"
input_device int 2 Audio input device index (null = default)
confidence_threshold float 0.5 Wake word sensitivity (0.0-1.0, higher = stricter)
inference_interval int 10 Frames between inferences (openWakeWord only)
trigger_cooldown_seconds float 2.0 Seconds between wake word triggers
custom_keywords list ["hey assistant", "computer"] Custom wake words
sample_rate int 16000 Audio sample rate (reserved)
energy_fallback.threshold float 0.02 Energy threshold for fallback detection
energy_fallback.required_chunks int 3 Consecutive chunks above threshold to trigger

asr Section

Key Type Default Description
engine string "vosk" Speech recognition engine
model_path string "./models/vosk-model-small-en-us-0.15" Path to Vosk model folder
sample_rate int 16000 Audio sample rate (reserved)
record_seconds int 5 Fallback recording duration in seconds
input_device int 2 Audio input device index
vad_enabled bool true Enable voice activity detection
vad_silence_duration_ms int 800 Milliseconds of silence to stop recording
vad_min_record_seconds float 1.0 Minimum recording duration
vad_max_record_seconds float 10.0 Maximum recording duration
vad_energy_threshold float 0.01 Energy threshold for speech detection
vad.enabled bool true Legacy VAD setting
vad.silence_threshold float 0.005 Legacy silence threshold
vad.silence_duration float 1.2 Legacy silence duration
vad.max_record_duration float 10.0 Legacy max duration
audio_enhancement.enabled bool true Apply audio enhancements
audio_enhancement.noise_reduction bool true Apply high-pass filter
audio_enhancement.normalization bool true Normalize audio levels
audio_enhancement.volume_boost float 1.5 Volume multiplier

nlu Section

Key Type Default Description
use_ollama bool false Enable LLM fallback for unknown intents
use_keyword_commands bool true Reserved for keyword-only mode
ollama_model string "llama3.2" Ollama model name
ollama_base_url string "http://localhost:11434" Ollama API endpoint
max_history int 10 Conversation history length
fallback_to_llm bool true Reserved (fallback always enabled)
log_intents bool true Reserved for intent logging
fuzzy_match_threshold float 0.6 Word overlap threshold for fuzzy app matching
unknown_intent_response string "Sorry, I didn't..." Response for unrecognized input
intent_patterns dict {...} Reserved (patterns hardcoded in handlers)

tts Section

Key Type Default Description
engine string "pyttsx3" TTS engine: "pyttsx3" or "edge-tts"
edge_voice string "en-US-JennyNeural" edge-tts voice name
rate float 1.0 Speech rate multiplier
volume float 1.0 Reserved for volume control
confirm_reminders bool true Reserved for confirmation UI
confirm_actions bool true Reserved for confirmation UI

assistant Section

Key Type Default Description
continuous_listening bool false Keep listening after each command
audio_feedback bool true Play sounds on wake word detection
confirm_destructive bool true Confirm before shutdown/restart

actions Section

Key Type Default Description
screenshot_folder string "screenshots" Directory to save screenshots
file_search_roots list ["C:\Users"] Root directories for file search
file_search_max_results int 5 Maximum file search results
file_search_timeout_seconds int 30 Timeout for file search in seconds
weather_api_key string "" OpenWeatherMap API key
weather_default_city string "New York" Default city for weather queries
weather_units string "metric" Weather units: "metric" or "imperial"
allow_sleep bool true Allow putting PC to sleep
web_search_url string "https://google.com/search?q=" Search engine URL
apps dict {...} App name → command mappings
websites dict {...} Site name → URL mappings

reminders Section

Key Type Default Description
enabled bool true Enable reminder functionality
storage_path string "reminders.json" Reminder persistence file
check_interval_seconds int 30 Seconds between reminder checks
notification_timeout int 10 Desktop notification duration
tts_notification bool true Speak reminder via TTS

ui Section

Key Type Default Description
enabled bool false Enable PyQt6 GUI
enable_tray bool false Enable system tray icon
show_window_on_start bool false Show window on startup
minimize_to_tray bool false Minimize to tray on close
start_minimized bool false Start minimized
confirm_exit bool true Confirm before exiting

tray Section

Key Type Default Description
enabled bool false Enable system tray
show_icon bool false Show tray icon
start_listening_on_start bool false Start listening on launch
tooltip string "PC Voice Assistant" Tray icon tooltip
icon_path string null Custom tray icon path

logging Section

Key Type Default Description
level string "INFO" Log level: DEBUG, INFO, WARNING, ERROR
file string "logs/assistant.log" Log file path
console bool true Also log to console
max_bytes int 5242880 Max log file size (5MB)
backup_count int 5 Number of backup logs to keep
show_logs_on_tray bool true Log viewer in tray menu

Reserved/Unused Config Keys

These keys exist in config.json but are not currently read by Python code. They are preserved for future use:

Section Key Planned Use
asr whisper_model Future Whisper ASR integration
asr sample_rate Reserved for sample rate configuration
nlu use_keyword_commands Keyword-only mode flag
nlu fallback_to_llm LLM fallback control
nlu log_intents Intent usage logging
nlu intent_patterns Configurable intent patterns
tts volume TTS volume control
tts confirm_reminders Reminder confirmation UI
tts confirm_actions Action confirmation UI
wake_word custom_model_paths Custom wake word models
wake_word pronunciation_hints Pronunciation tuning
wake_word sample_rate Sample rate configuration

5. Voice Commands Reference

Greetings

Command Example
hello "Hello"
hey "Hey there"
good morning "Good morning"

Time & Date

Command Example
what time is it "What time is it?"
what's the date "What's today's date?"
uptime "How long has the PC been running?"

Volume Control

Command Example
volume up "Volume up"
volume down "Turn down the volume"
mute "Mute"
unmute "Unmute"

Applications & Websites

Command Example Notes
open notepad "Open Notepad" Exact match
open calculator "Launch Calculator" Exact match
open chrome "Open Chrome" Fuzzy matched to "google chrome"
open vs code "Open VS Code" Fuzzy matched to "visual studio code"
go to github "Go to GitHub" Opens URL from config

Web Search

Command Example
search for "Search for Python tutorials"
look up "Look up the weather"
find "Find recipes for pasta"

Reminders

Command Example
in X minutes "Remind me in 10 minutes"
at X time "Remind me at 5 pm"
tomorrow at "Remind me tomorrow at 9am"
in X hours "Remind me in 2 hours"
in X hours and Y minutes "Remind me in 1 hour and 30 minutes"
in X seconds "Remind me in 90 seconds"
next weekday at "Remind me next Monday at 3pm"
at noon "Remind me at noon"
at midnight "Remind me at midnight"

Actions

Command Example
screenshot "Take a screenshot"
search "Search for [query]"
empty trash "Empty the recycle bin"
processes "Show running processes"

Math

Command Example
plus/add "What is 5 plus 3?"
minus/subtract "Calculate 10 minus 4"
times/multiply "What is 3 times 7?"

System Control

Command Example
shutdown "Shutdown"
restart "Restart"
sleep "Sleep"

Conversation

Command Example
thank you "Thank you"
goodbye "Goodbye"
help "Help"
who are you "Who are you?"

6. Architecture Overview

Pipeline Flow

┌─────────────────────────────────────────────────────────────────┐
│                         MAIN LOOP                               │
├─────────────────────────────────────────────────────────────────┤
│  1. WAKE WORD DETECTION (wake_word.py)                         │
│     - Listen for wake word ("alexa", "hey computer")          │
│     - Uses openWakeWord ML model or energy threshold           │
│     - Blocking wait with VAD to detect speech start           │
├─────────────────────────────────────────────────────────────────┤
│  2. RECORD AUDIO (asr.py)                                     │
│     - Record until silence detected (VAD)                       │
│     - Configurable min/max duration                            │
│     - Audio enhancement: noise reduction, normalization         │
├─────────────────────────────────────────────────────────────────┤
│  3. TRANSCRIBE (asr.py)                                       │
│     - Vosk offline ASR → text                                 │
│     - Fallback to keyword detection if model unavailable       │
├─────────────────────────────────────────────────────────────────┤
│  4. PARSE INTENT (nlu.py)                                     │
│     - Keyword-based pattern matching                           │
│     - Optional Ollama LLM for unknown intents                  │
│     - Intent handlers: apps, reminders, volume, etc.           │
├─────────────────────────────────────────────────────────────────┤
│  5. EXECUTE ACTION                                             │
│     - Open apps via subprocess                                 │
│     - Set reminders (persisted to JSON)                        │
│     - Control volume via PowerShell                           │
│     - Screenshot via mss                                       │
├─────────────────────────────────────────────────────────────────┤
│  6. SPEAK RESPONSE (tts.py)                                   │
│     - pyttsx3 (offline Windows SAPI)                          │
│     - edge-tts (online, higher quality)                       │
│     - Falls back gracefully if primary fails                   │
└─────────────────────────────────────────────────────────────────┘

Module Responsibilities

Module Purpose
main.py Entry point, main loop coordination, signal handling
wake_word.py Wake word detection (openWakeWord/energy fallback)
asr.py Audio recording (VAD), Vosk transcription, audio enhancement
nlu.py Intent parsing, action handlers, reminder scheduler
tts.py Text-to-speech (pyttsx3/edge-tts)
gui.py PyQt6 GUI window
tray.py System tray management
config_loader.py Configuration file loading (singleton)
config_validator.py Configuration validation
logging_config.py Logging setup with file rotation
audio_feedback.py Wake word beep sounds
performance_metrics.py Latency tracking
config.json All configuration values

7. Troubleshooting

Wake Word Never Triggers

Symptoms: Assistant doesn't respond to wake word.

Solutions:

  1. Check microphone is working in Windows Sound settings
  2. Verify wake_word.input_device in config.json is correct (or null for default)
  3. Check wake_word.engine is set to "openwakeword" or "energy"
  4. For openWakeWord: ensure it's installed (pip install openwakeword)
  5. For energy fallback: lower wake_word.energy_fallback.threshold (e.g., 0.005)
  6. Lower wake_word.confidence_threshold (e.g., 0.3)
  7. Speak clearly and closer to the microphone

ASR Model Not Found Error

Symptoms: "Vosk model not found" error on startup.

Solutions:

  1. Download model from https://alphacephei.com/vosk/models
  2. Extract to project root, ensure folder structure:
    models/vosk-model-small-en-us-0.15/{am,conf,graph,ivector}/
    
  3. Verify asr.model_path in config.json matches folder name
  4. Check the path doesn't contain special characters

TTS Makes No Sound

Symptoms: Assistant speaks but you hear nothing.

Solutions:

  1. Check tts.engine in config.json
    • pyttsx3: Uses Windows SAPI, check system volume
    • edge-tts: Requires internet connection
  2. Run python -c "import tts; print(tts.get_engine_info())" to see active engine
  3. Install pyttsx3 if missing: pip install pyttsx3
  4. For edge-tts: pip install edge-tts
  5. Check Windows audio device is not muted

Ollama Intents Not Working

Symptoms: Unknown commands return fallback instead of LLM response.

Solutions:

  1. Confirm Ollama is running:
    ollama serve
    
  2. Check model is installed:
    ollama list
    
  3. Pull model if missing:
    ollama pull llama3.2
    
  4. Verify nlu.use_ollama is true in config.json
  5. Check nlu.ollama_base_url is http://localhost:11434
  6. Test manually:
    curl http://localhost:11434/api/tags
    

Reminder Notification Not Showing

Symptoms: Reminder fires but no desktop notification.

Solutions:

  1. Install plyer for desktop notifications:
    pip install plyer
    
  2. Check Windows notification settings allow notifications from Python
  3. Without plyer, reminders still work via TTS voice notification
  4. Check reminders.enabled is true in config.json

App Open Command Not Working

Symptoms: "I couldn't find an app" response.

Solutions:

  1. Check the app exists in actions.apps in config.json
  2. Add the app with full path if needed:
    "notepad": "C:\\Windows\\notepad.exe"
    
  3. Fuzzy matching handles partial names:
    • "open chrome" matches "google chrome"
    • "open vs code" matches "visual studio code"
  4. For URLs, add to actions.websites:
    "youtube": "https://youtube.com"
    

8. Running Tests

python -m pytest tests/ -v

Expected Result: 133 tests, all passing.

Test Coverage

Test File Tests Coverage
test_asr.py 16 ASR configuration, VAD, transcription
test_config.py 7 Config loading, validation
test_main.py 7 Main loop error recovery, pause/resume state
test_nlu.py 77 Intent handlers, reminders, fuzzy matching, clipboard, file search, weather
test_tray.py 3 System tray lifecycle
test_tts.py 13 TTS engines, edge cases
test_wake_word.py 10 Wake word detection, energy fallback

9. Adding New Intents

Step 1: Add App/Command to Config (if applicable)

For new apps or websites, add to config.json:

"actions": {
    "apps": {
        "myapp": "C:\\path\\to\\myapp.exe"
    }
}

Step 2: Create Intent Handler Class

In nlu.py, add a new handler class:

class NewIntentHandler(IntentHandler):
    def __init__(self):
        super().__init__("newintent", ["trigger word", "other trigger"])
    
    def handle(self, text: str) -> Optional[str]:
        # Your logic here
        return "Response message"

Step 3: Register the Handler

Add to INTENT_HANDLERS list in nlu.py:

INTENT_HANDLERS = [
    ...
    NewIntentHandler(),
]

Step 4: Add Tests

In tests/test_nlu.py:

def test_new_intent_handler(self):
    handler = nlu.NewIntentHandler()
    assert handler.match("trigger word")
    result = handler.handle("trigger word")
    assert "Response" in result

Step 5: Update README

Add your command to the Voice Commands Reference section.

Step 6: Run Tests

python -m pytest tests/ -v

10. Changelog

Version 1.1.0 — Current Development

New Features

  • VAD Silence Detection: Recording now stops automatically when you stop speaking, instead of fixed duration. Configurable min/max duration and silence threshold.
  • Fuzzy App Matching: "open chrome" automatically matches "google chrome" in config. Uses word overlap algorithm with configurable threshold.
  • Extended Reminder Parsing: Natural time expressions now supported:
    • "in 2 hours"
    • "in 1 hour and 30 minutes"
    • "tomorrow at 9am"
    • "next monday at 3pm"
    • "in 90 seconds"
  • Unknown Intent Fallback: Customizable response when no intent matches. Configurable via nlu.unknown_intent_response.

Reliability Improvements

  • Config Caching: Config is now loaded once per module, not on every function call. Significant performance improvement.
  • Atomic Reminder Writes: Uses temp file + os.replace() to prevent corruption if process is killed mid-write.
  • Wake Word Race Condition Fix: Added _model_ready Event to properly synchronize background model loading.
  • TTS MP3/WAV Fix: edge-tts generates MP3 but was being played as WAV. Now uses msedge.exe for correct playback.
  • Path Quoting Fix: PowerShell commands now properly escape paths with spaces.

Security Improvements

  • subprocess shell=True Removal: All subprocess calls now use list-based arguments, eliminating shell injection risk.

Testing

  • 133 Test Suite: Comprehensive edge case coverage added:
    • ASR: mic failure, missing model, empty transcription, partial results
    • TTS: missing files, long strings, engine fallback
    • Wake word: timeout fallback, model loading errors
    • Main loop: error recovery from all stages
    • Reminders: concurrent writes, persistence, past time handling

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pc_voice_assistant-1.1.0.tar.gz (20.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pc_voice_assistant-1.1.0-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

Details for the file pc_voice_assistant-1.1.0.tar.gz.

File metadata

  • Download URL: pc_voice_assistant-1.1.0.tar.gz
  • Upload date:
  • Size: 20.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pc_voice_assistant-1.1.0.tar.gz
Algorithm Hash digest
SHA256 2a46debf2d905fd5efe89ac52f731ab7516b10088b425784c4e44779211da0aa
MD5 d5ffecc7ca7d3bcd524d3041f87c413a
BLAKE2b-256 0ba561d40142534a55ad12566bc53509c7dd2b533d4b04f8dba314dded86e8c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for pc_voice_assistant-1.1.0.tar.gz:

Publisher: publish.yml on epicnellson/pc-assistant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pc_voice_assistant-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pc_voice_assistant-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 115879be44d1a33adcb5843f987df82791c4d5bc2e9b52f5fdb2d28d04ba0ec5
MD5 b5c649ca498b535630f69f5c896b39dd
BLAKE2b-256 bed6c66bae5600a67838a11bbc8850df7d2996ee9e2e7e6ee495b857174c19bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for pc_voice_assistant-1.1.0-py3-none-any.whl:

Publisher: publish.yml on epicnellson/pc-assistant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page